The Default Shell
Ever so often, people publish blog posts that tout the Z shell as the best thing since sliced bread. I also know two guys who use it on a daily base. Now, as you could imagine, I am a gadgety kind of person, try new things out and usually “optimize” my workflows but up to now, I was not convinced to switch entirely. There are two reasons for my reluctance:
- Bash is the default shell on any Unix-like system I encountered so far.
- Most of the zsh features I consider helpful, are already implemented in a way with the Bash or can be simply extended with little code.
I don’t want to convince anyone to switch from zsh to Bash with this post but just demonstrate some of the things in my .bashrc that could be considered as zsh builtin alternatives.
Completion
One of the most cited examples of zsh’s superiority is tab completion. The guy in the post gives a peek on what’s possible:
It knows which commands git takes, which hosts are in my hosts file for ssh, which users my system have when I write chmod, available packages to apt-get, etc.
When I read that, I was puzzled, because everything is possible with the
/etc/bash_completion
script and auxiliary completion scripts in
/etc/bash_completion.d/
. On debian-based systems, this is enabled
out-of-the-box, but in general you would just source the script in your
.bashrc
like this:
if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
. /etc/bash_completion
fi
What is nice about the zsh completion though, is its navigation-style choice
of different tab-completed options, so instead of cd /usr/share/<tab><tab><tab>
ad infinitum you can tab once and navigate much
faster between the different completion options.
Prompt
From what I can see, zsh prompts tend to be colorful and almost always overloaded with information. Personally, I just need the absolute minimum: user name, host (especially when connecting to different machines), and the current directory. However, I also like to have the path to the current working directory truncated to maximize the input line. Unfortunately, I have no idea where this piece of kind originates from, but this is what accomplishes it:
function _prompt_workingdir ()
{
local pwdmaxlen=$(($COLUMNS/5))
local trunc_symbol="..."
if [[ $PWD == $HOME* ]]; then
newPWD="~${PWD#$HOME}"
else
newPWD=${PWD}
fi
if [ ${#newPWD} -gt $pwdmaxlen ]; then
local pwdoffset=$(( ${#newPWD} - $pwdmaxlen + 3 ))
newPWD="${trunc_symbol}${newPWD:$pwdoffset:$pwdmaxlen}"
fi
echo $newPWD
}
zsh (or rather the plugin collection oh-my-zsh?) is also capable of visualizing Git status information when inside a Git-versioned directory. This is also possible within Bash using this fine and fast snippet from Jo Liss:
function _git_prompt()
{
local git_status="`git status --porcelain 2>&1`"
if ! [[ "$git_status" =~ Not\ a\ git\ repo ]]; then
if [ -z "$git_status" ]; then
local ansi=42
elif [[ "$git_status" == *"??"* ]]; then
local ansi=43
else
local ansi=45
fi
local branch=$(__git_ps1 "%s")
test "$branch" != master || branch=' '
echo -n '\[\e[0;37;'"$ansi"';1m\]'"$branch"'\[\e[0m\] '
fi
}
So, I plug these two functions inside my usual prompt like this
PS1="`_git_prompt`"'\[\033[1;30m\]me\[\033[0m\]@\[\033[1;30m\]\h\[\033[0m\]:\[\033[0;33m\]$(_prompt_workingdir)\[\033[0m\] '
and achieve this
So, that is my prompt. Context-dependent information and as simple as possible.
Navigation
Instead of changing directories with cd
1 and tab-completion I usually use
the fantastic [autojump][] tool that changes to the directory that matches with
what is supplied as arguments. It needs to learn the paths by going in with cd
but this initial learning phase pays off relatively soon, especially when
changing into deep hierarchies and when confronted with MS-style uppercased
paths. For example,
$ cd /usr/share/gtk-doc/html/gtk3
becomes
$ j u gtk3
Lo and behold!, autojump also supports zsh.
Okay, I lied. Sometimes, I use it to go into the next child directory. Or
cd -
into the previous directory. That’s pretty nifty.
[autojump]: https://github.com/joelthelion/autojump
Command-line editing
In terms of command-line editing both shells provide similar, powerful modes. By default both use a scheme similar to Emacs, but can also operate with a Vi-like interface. I only recently began to explore command-line editing and my fingers are not yet accustomed to it. Anyway, here are some common strokes:
<M-b>
/<M-f>
: move by word<C-b>
/<C-f>
: move by character<C-p>
/<C-n>
: previous/next historic command line<C-a>
/<C-e>
: go to beginning/end of the line<C-u>
/<C-k>
: delete from current cursor position to the beginning/end
You should know them not only because they are easier to type than the arrow and
page keys, but because they are available to any application that offers a
command-line interface based on readline. For example, to rename a tmux window I
am much faster with <C-a>,<C-u>
than using the backspace key.
So, this is basically my Bash setup with some simple yet powerful enhancements to the stock experience. I am pretty satisfied with this setup but if you have anything that might convince me to switch to zsh, don’t hesitate to tell me.