Why zsh Should Be the Default Shell on OS X

From OS X Scientific Computing

Jump to: navigation, search
Return to ZSH on OS X.

I used to think people who argued about the merits of different unix shells were, well, blessed with a lot of spare time. However, I was finally convinced by Gary Kerbaugh, an immensely knowledgable and helpful person, to give zsh a try. I decided to do so for a weekend, and found I was unable to go back. Under OS X v. 10.2.x, tcsh is the default shell. The other shells that come with OS X by default include bash (the "Bourne Again" and more user-friendly version of the Bourne shell, sh) and zsh. zsh is probably most like ksh in its syntax, but is actually more user-friendly than is tcsh.

If you are comfortable with tcsh, you will likely find zsh at least as user-friendly. If you like any of the sh-type shells, you will find that zsh incorporates all of the programmability of the sh-type shells but has some very nice additional features.

The zsh site does an excellent job of explaining and advocating for the Z-shell. Be sure to obtain a copy of the very nice pdf. You should download and read it. I printed it out and had it bound at a photocopy store. It is one of the best-written computer books I have ever read. In addition, you can now purchase a book entitled From Bash to Z Shell. (Oliver Kiddle, Jerry Peek and Peter Stephenson, From Bash to Z Shell: Conquering the Command Line, Apress (2005).)

Here are some of the things that zsh offers you that tcsh (and maybe other options) do not:


Really nice programmable command-line completions, including remote filename completions:

For example, let's say I have nine png files in a directory that start with the prefix aqua. If I type open aqua and then hit the tab key without hitting the space bar, I will see the following output from the shell:

 zsh-% open aqua<tab>
 ---- file
 aqua_autoinstall_osx.png    aqua_devtools_osx.png         aqua_mosflm_osx.png
 aqua_title_crystal_o.png      aqua_backups_osx.png         aqua_eden_osx.png
 aqua_nmr_osx.png              aqua_title_crystal_osx.png     aqua_ccp4_osx.png

But it gets even better. If I hit the tab key again and again (six times total) it cylces through the various options in the order listed, giving me the sixth file:

open aqua <tab><tab><tab><tab><tab><tab>

open aqua_eden_osx.png

Also, the shell is smart enough to use only sensible options, thanks to the programmability of the command line completion functions. Nonsensical completions are usually filtered out.

You aren't limited to filenames. Zsh can complete just about anything, including user and domain names:

 zsh-% ssh wgs<tab>

gives me

 zsh-% ssh wgscott@


 zsh-% ssh wgscott@x<tab>

gives me

 ssh wgscott@xaxaxa.foobar.org

Again, it can cycle through all the options. It even does remote file completion with scp, (but you have to have passwordless ssh set up first for it to work efficiently).

You can define useful functions in addition to aliases:

The various sh-shells, unlike csh and tcsh, allow you to define functions. A function is essentially like an alias to a zsh shell script that you can call by name, except that it is read into memory when the shell is started. It is much more versatile than a simple alias as well. Here is a somewhat trivial example (that probably could be an alias in tcsh):

function joinpdf {
          gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=merged.pdf "$@"

This function, called joinpdf, takes one or any number of pdf files and joins them into one catenated pdf called merged.pdf.

Here is another example, a function that invokes vi as a normal user unless one or more files is owned by root, in which case it invokes sudo vi. I wrote this (with a lot of help from Gary Kerbaugh) because I got really tired of opening a root owned file with vi, making a bunch of changes, and then finding I couldn't save the changes. This would be too complex for a simple alias, and being a function rather than a shell-script, it is invoked a bit more quickly.

 function vi {
         for ((i = 1; i <= $LIMIT; i++ )) do
                 eval file="\$$i"
                 if [[ -e $file && ! -O $file ]]
         if [[ $otherfile = 1 ]]
                 sudo vi "$@"
                 command vi "$@"

Recursive globbing is implemented in zsh:


ls **/*whatever.c

finds your forwhatever.c file anywhere in the directory tree.

Glob qualifiers are also available in zsh:

ls *(/)

prints directories and their contents

print  *(/)

prints the directory names

ls *(.)

lists just files.

Admitedly, you can do stuff in tcsh like

alias dls "\ls -F | grep /"

but this is way more cool and flexible.

Return to ZSH on OS X.
Personal tools