As has already been mentioned, zsh is most similar to ksh, while many of the additions are to please csh users. Here are some more detailed notes. See also the article `UNIX shell differences and how to change your shell' posted frequently to the USENET group comp.unix.shell.
Most features of ksh (and hence also of sh) are implemented in zsh; problems can arise because the implementation is slightly different. Note also that not all ksh's are the same either. I have based this on the 11/16/88f version of ksh; differences with ksh93 will be more substantial.
As a summary of the status:
ARGV0=sh zsh
will also work);
The classic difference is word splitting, discussed in 3.1; this catches out very many beginning zsh users. As explained there, this is actually a bug in every other shell. The answer is to set SH_WORD_SPLIT for backward compatibility. The next most classic difference is that unmatched glob patterns cause the command to abort; set NO_NOMATCH for those.
Here is a list of various options which will increase ksh compatibility, though maybe decrease zsh's abilities: see the manual entries for GLOB_SUBST, IGNORE_BRACES (though brace expansion occurs in some versions of ksh), KSH_ARRAYS, KSH_OPTION_PRINT, LOCAL_OPTIONS, NO_BAD_PATTERN, NO_BANG_HIST, NO_EQUALS, NO_HUP, NO_NOMATCH, NO_RCS, NO_SHORT_LOOPS, PROMPT_SUBST, RM_STAR_SILENT, POSIX_BUILTINS, SH_FILE_EXPANSION, SH_GLOB, SH_OPTION_LETTERS, SH_WORD_SPLIT (see question 3.1) and SINGLE_LINE_ZLE. Note that you can also disable any built-in commands which get in your way. If invoked as `ksh', the shell will try and set suitable options.
Here are some differences from ksh which might prove significant for ksh programmers, some of which may be interpreted as bugs; there must be more. Note that this list is deliberately rather full and that most of the items are fairly minor. Those marked `*' perform in a ksh-like manner if the shell is invoked with the name `ksh', or if `emulate ksh' is in effect. Capitalised words with underlines refer to shell options.
array[0]
refers to array[1]
;
$array
refers to the whole array, not $array[0]
;
braces are unnecessary: $a[1] == ${a[1]}
, etc.
The KSH_ARRAYS option is now available.
coproc
; |&
behaves like
csh.
cmd1 && cmd2 &
, only cmd2
instead of the whole
expression is run in the background in zsh. The manual implies
this is a bug. Use { cmd1 && cmd2 } &
as a workaround.
foo="*"; print $foo
prints all files in ksh but *
in zsh.
(GLOB_SUBST has been added to fix this.)
$(echo '\$x')
is treated differently: in ksh, it
is not stripped, in zsh it is. (The `...`
form gives the same in
both shells.)
$PSn
do not do parameter substitution by default (use PROMPT_SUBST).
^
, ~
and #
(but not |
)forms require EXTENDED_GLOB.
[1] Note that ~
is the only globbing operator to have a lower
precedence than /
. For example, **/foo~*bar*
matches any
file in a subdirectory called foo
, except where bar
occurred somewhere in the path (e.g. users/barstaff/foo
will
be excluded by the ~
operator). As the **
operator cannot
be grouped (inside parentheses it is treated as *
), this is
the way to exclude some subdirectories from matching a **
.
:
s (intended for
PATHs).
integer
does not allow -i
.
$ENV
variable (use /etc/zshrc
, ~/.zshrc
;
note also $ZDOTDIR
).
$PATH
is not searched for commands specified
at invocation without -c.
emacs
, gmacs
, viraw
are not supported.
Use bindkey to change the editing behaviour: set -o {emacs,vi}
becomes bindkey -{e,v}
; for gmacs, go to emacs mode and use
bindkey \^t gosmacs-transpose-characters
.
keyword
option does not exist and -k
is instead
interactivecomments. (keyword
will not be in the next ksh
release either.)
\
does not escape editing chars (use ^V
).
<ESC>#
; try <ESC>q
).
#
in an interactive shell is not treated as a comment by
default.
r
, autoload
, history
, integer
...)
were aliases in ksh.
alias
newgrp="exec newgrp"
jobs
has no -n
flag.
read
has no -s
flag.
let "i = foo"
, foo is evaluated as a number, not an
expression (in let "i = $foo"
, however, it is treated as an
expression).
select
always redisplays the list of selections on each loop.
Although certain features aim to ease the withdrawal symptoms of csh (ab)users, the syntax is in general rather different and you should certainly not try to run scripts without modification. The c2z script is provided with the source (in Misc/c2z) to help convert .cshrc and .login files; see also the next question concerning aliases, particularly those with arguments.
Csh-compatibility additions include:
logout
, rehash
, source
, (un)limit
built-in commands.
*rc
file for interactive shells.
cshjunkie*
, ignoreeof
options.
>&
, |&
etc. redirection.
(Note that >file 2>&1
is the standard Bourne shell command for
csh's >&file
.)
foreach ...
loops; alternative syntax for other loops.
if ( ... ) ...
, though this still doesn't
work like csh: it expects a command in the parentheses. Also
for
, which
.
$PROMPT
as well as $PS1
, $status
as well as $?
,
$#argv
as well as $#
, ....
%
for prompts.
$PATH
etc. are colon-separated, $path
are arrays.
!
-type history (which may be turned off via setopt
nobanghist
).
First of all, check you are using the syntax
Otherwise, your aliases probably contain references to the command
line of the form \!*
, etc. Zsh does not handle this behaviour as it
has shell functions which provide a way of solving this problem more
consistent with other forms of argument handling. For example, the
csh alias
~
). In fact, this problem is
better solved by defining the special function chpwd() (see the manual).
Note also that the ;
at the end of the function is optional in zsh,
but not in ksh or sh (for sh's where it exists).
Here is Bart Schaefer's guide to converting csh aliases for zsh.
\!:1
, \!*
etc.),
then in zsh you need a function (referencing $1
, $*
etc.).
Otherwise, you can use a zsh alias.
$*
in the body (inside the { }
). Parameters don't magically
appear inside the { }
the way they get appended to an alias.
alias rm "rm -i"
),
then in a zsh function you need the "command" keyword
(function rm() { command rm -i $* }
), but in a zsh alias
you don't (alias rm="rm -i"
).
alias ls "ls -C";
alias lf "ls -F" ==> lf == ls -C -F
) then you must either:
Those first four are all you really need, but here are four more for heavy csh alias junkies:
$1
, $2
, etc.) greater than the number of
parameters. (E.g., in a csh alias, a reference to \!:5
will
cause an error if 4 or fewer arguments are given; in a zsh
function, $5
is the empty string if there are 4 or fewer
parameters.)
alias --
:
alias -g
in zsh until you REALLY know what
you're doing.
There is one other serious problem with aliases: consider
l
in the function definition is in command position and is expanded
as an alias, defining /bin/ls
and -F
as functions which call
/bin/ls
, which gets a bit recursive. This can be avoided if you use
function
to define a function, which doesn't expand aliases. It is
possible to argue for extra warnings somewhere in this mess. Luckily,
it is not possible to define function
as an alias.
Bart Schaefer's rule is: Define first those aliases you expect to use in the body of a function, but define the function first if the alias has the same name as the function.
(The sections on csh apply too, of course.) Certain features have
been borrowed from tcsh, including $watch
, run-help
, $savehist
,
$histlit
, periodic commands etc., extended prompts, sched
and which
built-ins. Programmable completion was inspired by,
but is entirely different to, tcsh's complete
. (There is a perl
script called lete2ctl
in the Misc directory of the source
distribution to convert complete
to compctl
statements.)
This list is not definitive: some features have gone in the other
direction.
If you're missing the editor function run-fg-editor
, try something
with bindkey -s
(which binds a string to a keystroke), e.g.
bindkey -s
allows limitless possibilities along these lines. You can execute
any command in the middle of editing a line in the same way,
corresponding to tcsh's -c
option:
\eq
saves the current input line to
be restored after the command runs; a better effect with multiline
buffers is achieved if you also have
The Bourne-Again Shell, bash, is another enhanced Bourne-like shell; the most obvious difference from zsh is that it does not attempt to emulate the Korn shell. Since both shells are under active development it is probably not sensible to be too specific here. Broadly, bash has paid more attention to standards compliancy (i.e. POSIX) for longer, and has so far avoided the more abstruse interactive features (programmable completion, etc.) that zsh has.
People often ask why zsh has all these `unnecessary' csh-like features, or alternatively why zsh doesn't understand more csh syntax. This is far from a definitive answer and the debate will no doubt continue.
Paul's object in writing zsh was to produce a ksh-like shell which would have features familiar to csh users. For a long time, csh was the preferred interactive shell and there is a strong resistance to changing to something unfamiliar, hence the additional syntax and CSH_JUNKIE options. This argument still holds. On the other hand, the arguments for having what is close to a plug-in replacement for ksh are, if anything, even more powerful: the deficiencies of csh as a programming language are well known (look in any Usenet FAQ archive, e.g. http://www.cis.ohio-state.edu/hypertext/faq/usenet/unix-faq/\ shell/csh-whynot/faq.html if you are in any doubt) and zsh is able to run many standard scripts such as /etc/rc.
Of course, this makes zsh rather large and feature-ridden so that it seems to appeal mainly to hackers. The only answer, perhaps not entirely satisfactory, is that you have to ignore the bits you don't want. The introduction of loadable in modules in version 3.1 should help.