Jun
17
Moving my .bashrc to .zshrc
Filed Under Computers & Tech, System Administration on June 17, 2019 at 2:29 pm
Having used Zsh rather than Bash for over a week it was time to make the move permanent by migrating my shell customisations from ~/.bashrc
to ~/.zshrc
. Your milage may vary, but I was pleased to find I didn’t need to make any changes, and, that I could get rid of one command from the script because Zsh defaults to a behaviour I had to explicitly opt in to with Bash.
TL;DR: environment variables (including PATH
) and aliases work just the same in Zsh as they do in Bash, so if those are the only things you alter in your ~/.bashrc
, then you can just copy it over to ~/.zshrc
. But, if you alter Bash settings in your ~/.bashrc
, you’ll need figure out the equivalent Zsh options and replace the relevant lines with the appropriate Zsh setopt
or unsetopt
Zsh commands.
Note: The original version of this post wrongly asserted that Zsh avoided shell history duplication by default. This is not correct, and the post was updated with the details of how to update your ~/.zshrc
file to avoid duplication of commands when you hit the up arrow in the shell.
My ~/.bashrc
file did just three things:
- Export environment variables
- Set command aliases
- Set non-default Bash Options
Environment Variables
As an example, my ~/.bashrc
sets my SVN editor to vi
by exporting an environment variable:
# set the editor for SVN export SVN_EDITOR=/usr/bin/vi
Copying these lines un-changed to ~/.zshrc
worked perfectly.
Command Aliases
As an example, my ~/.bashrc
defines an alias to replace ls
with ls -GF
so I get colour-coded output when I list the contents of folders:
# Get LS to work in colour alias ls="ls -GF"
Again, copying these lines un-changed to ~/.zshrc
worked perfectly.
Non-default Bash Options
On the whole I chose not to alter Bash’s default behaviour, but I did make one exception to that rule.
On MacOs, when you enter the same command multiple times in a row in the same terminal window each copy of the command gets saved into the Bash history, so, when you hit the up arrow to get to previous commands, you have to hit up repeatedly to get to the command you entered before the one you repeated. I can’t comprehend why anyone would want this behaviour as the default, so, I changed it!
Bash uses environment variables to control shell options, so, my ~/.bashrc
contains the following lines to stop the duplication of repeated commands in the history:
# stop bash saving duplicates to the history export HISTCONTROL=ignoredups
Zsh doesn’t manage its optional settings via environment variables, it provides dedicated commands for that purpose — setopt
to set an option, and unsetopt
to un-set an option.
In general, you’ll need to find a matching Zsh option for each of your custom Bash options, and then add the appropriate setopt
or unsetopt
command into your ~/.zshrc
.
In this specific case, I needed to find the Zsh option that’s equivalent to HISTCONTROL=ignoredups
in Bash.
In both Bash and Zsh, when you hit the up arrow you are working your way through the shell’s history. Setting HISTCONTROL=ignoredups
in Bash tells the shell not to add a command to the history if it’s the same as the previous command added to the history.
The history file is not just used for the up arrow, it also serves as a record of the commands executed. By telling Bash to ignore duplicates that history is actually incomplete, so this is an imperfect solution. The problem is we are filtering the writes to the history, not the reads from the history.
Zsh gives us much better options for controlling the history.
If we want to filter what gets written to the history we can use the option HIST_IGNORE_DUPS
to tell the shell not to store a command into the history if it’s a duplicate of the previously executed command. Or, we can be even more aggressive and use the HIST_IGNORE_ALL_DUPS
option to tell the shell not to store a command into the history if it already exists anywhere within the entire history.
A much better option IMO is Zsh’s HIST_FIND_NO_DUPS
option. This does not filter what gets written to the history, but rather, what gets read back from the history when we do thinks like hit the up arrow. Do note that this filter does not just filter contiguous repeats, but repeats anywhere in in the history. This is how the zshoptions
man page (man zshoptions) describes the option:
HIST_FIND_NO_DUPS:
When searching for history entries in the line editor, do not display duplicates of a line previously found, even if the duplicates are not contiguous.
I decided that of the three options, HIST_FIND_NO_DUPS
made the most sense for me, so I added the following to my ~/.zshrc
file:
# prevent duplicates when hitting the up arrow in the shell setopt HIST_FIND_NO_DUPS
Update — 26 September 2019: HIST_FIND_NO_DUPS:
is not working for me on MacOS Mojave, so switched to HIST_IGNORE_DUPS:
:
# prevent duplicates when hitting the up arrow in the shell setopt HIST_IGNORE_DUPS
Hope you keep up this series Bart and when you have enough, consider an episode n+1 of taming the terminal!