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 it 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. (Other shells that
you can invoke on OS X include sh and csh, but these are really
bash and tcsh. Take a look.)
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 likely 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 zsh. There is a new,
excellent book available called
From Bash to Z Shell, and there is also a very nice Free user
guide available as a pdf zsh
user guide that you should download and read.
Gary Kerbaugh has kindly provided a very nice set of startup files for
zsh.
Building on these, I created my own set of zsh
startup files.
Here are some of the things that zsh offers you that tcsh (and
maybe other options) do not:
1. Really nice
command-line completions:
If I have a bunch of files that start with aqua*, and I type open
aqua and hit the tab key, here is what I see:
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*
aqua_fink_osx.png*
aqua_othersites_osx.png*
aqua_unixlinks_osx.png*
aqua_cns_osx.png*
aqua_moldisp_osx.png*
aqua_otherstuff_osx.png*
aqua_xwindows_osx.png*
But it gets even better. If I hit the tab key again, it
cylces through the various options in the order listed:
open aqua
<tab><tab>
<tab><tab><tab><tab>
open
aqua_eden_osx.png
also, it is smart enough to use only sensible options.
You aren't limited to filenames. It can complete just about
anything, including user and domain names
zsh-% ssh wgs<tab> gives me
zsh-% ssh wgscott@
and
zsh-% ssh wgscott@h<tab>
gives me
ssh wgscott@hydrogen.ucsc.edu
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 properly.
2. You can define
useful functions in addition to aliases:
The various sh-shelss, 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
trivial example (that probably could be an alias):
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 merge.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,
again) 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.
vi () {
LIMIT=$#
for ((i = 1; i <=
$LIMIT; i++ )) do
eval file="\$$i"
if [[ -e $file && ! -O $file ]]
then
otherfile=1
else
fi
done
if [[ $otherfile = 1
]]
then
sudo vi "$@"
else
command vi "$@"
fi
}
3. Recursive
globbing:
eg:
ls **/*whatever.c
finds your forwhatever.c file anywhere in the directory tree.
4. Glob
qualifiers:
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.
Hi Bill,
The most fun
you can have with your clothes on, eh? This is exactly the kind of
"advertising" that makes a difference. I can't testify to ease of
switching because I had to put together the scripts. It's both
great news to occasional shell users and gratifying that it works
"out of the box" with my configuration scripts.
People
really have to see zsh in action to fully believe it. As you said
the completion mechanism is really a shining star. When you hit the
tab key and there are multiple possibilities, it lists the
possibilities by category. If you hit the tab key again multiple
times, zsh does the completion, cycling through the
possibilities.
The real
shock comes when I want to do something like "ssh
root@www.myhost.edu". I don't have a hostname starting with r so if
I type "ssh r" and hit the tab key, I get "ssh root@"! It knows
there is going to be a host following and adds the "@" after the
"root". Then, assuming only one host starting with "w", typing a w
and hitting the tab key results in the entire "ssh
root@www.myhost.edu". Even better, if the command is instead
scp, the last step above results in "scp root@www.myhost.edu:",
with the additional colon at the end, so that you only have to type
the filenames. Naturally zsh will complete the local
one.
A really
strange thing happened to me this morning. I was creating an alias
for p0f (thanks Sean!) and stupidly tried to hide single quotes
inside double quotes. It's not an error, the single quotes simply
aren't hidden. However, when I listed the alias, I found that zsh
had understood what I meant and had fixed it!! It saved it with
single quotes, closed that before my single quote, replaced my
single quotes with \', and then picked up with the single quoted
string again! Is that unbelievable or what?!
Thus, you
can also enjoy the fact that your initial excitement with zsh will
continually be rejuvenated! Thanks for helping me spread the
word!
-- Gary Kerbaugh
The
best way to learn about zsh is to use it. This is painless,
because you already have it installed on OS X. It is often
available on other platforms as well. I have also compiled
the latest version on OS X and on Solaris and the compilation was
painless. To use it, you can either
A. Invoke it from tcsh or any other shell by typing zsh -l at
the prompt, i.e.,
% zsh -l
The -l
(minus el) argument means that it will run through all the login
files as if you had invoked it initially. zsh -f invokes it
but explicitly without reading various files.
B. Create a term file or iTerm bookmark in which it is
invoked on startup.
C. Make it your default shell by editing
netinfo.
To best take advantage of what zsh has to offer, it is nice to
start with some startup files. Gary Kerbaugh has a
collection of zsh startup files that goes into
~/Library/init/zsh that you can download.
Building on these, I created my own set of zsh
startup files.