ZSH(1)
๐ NAME ๐
okay so the manual page is literally called ZSH(1) from the General Commands Manual ๐ง, and the thing it describes is zsh, the Z shell โจ
๐ฆ OVERVIEW ๐ฆ
okay bestie, zsh has SO many features that the manual got split into a bunch of little sections ๐ฅน here's the whole lineup:
zshโ Zsh overview (this section) ๐zshroadmapโ Informal introduction to the manual ๐บ๏ธzshmiscโ Anything not fitting into the other sections ๐ซzshexpnโ Zsh command and parameter expansion ๐ธzshparamโ Zsh parameters ๐zshoptionsโ Zsh options โ๏ธzshbuiltinsโ Zsh built-in functions ๐งฐzshzleโ Zsh command line editing โจ๏ธzshcompwidโ Zsh completion widgets ๐zshcompsysโ Zsh completion system ๐zshcompctlโ Zsh completion control ๐ฎzshmodulesโ Zsh loadable modules ๐งฉzshcalsysโ Zsh built-in calendar functions ๐zshtcpsysโ Zsh built-in TCP functions ๐zshzftpsysโ Zsh built-in FTP client ๐zshcontribโ Additional zsh functions and utilities ๐zshallโ Meta-man page containing all of the above ๐
๐ธ DESCRIPTION ๐ธ
okay so Zsh is a UNIX command interpreter (shell) โจ it's giving both an interactive login shell AND a shell script command processor, so you get the best of both worlds ๐ of the standard shells, zsh is lowkey the most similar to ksh, but it's packed with extra enhancements ๐ฅ also, no cap: in its default operating mode it does NOT try to be compatible with POSIX or other shells ๐ if you need that, check out the section Compatibility below ๐ซถ
zsh is absolutely loaded ๐คฉ it has command line editing, builtin spelling correction, programmable command completion, shell functions (with autoloading), a history mechanism, and a whole host of other features ๐ literally iconic.
๐ AUTHOR ๐
so the OG is Paul Falstad, who originally wrote zsh ๐งโโ๏ธ these days zsh is maintained by the members of the zsh-workers mailing list, <zsh-workers@zsh.org> ๐ the development is currently coordinated by Peter Stephenson, <pws@zsh.org> ๐ you can reach the coordinator at <coordinator@zsh.org>, but code stuff should generally go to the mailing list ๐
๐ AVAILABILITY ๐
zsh is available from the following HTTP and anonymous FTP sites ๐ฆ
ftp://ftp.zsh.org/pub/๐https://www.zsh.org/pub/๐
the up-to-date source code is on Git via Sourceforge โจ see https://sourceforge.net/projects/zsh/ for details ๐ซถ there's also a summary of instructions for the archive at https://zsh.sourceforge.io/ ๐
๐ MAILING LISTS ๐
zsh has several mailing lists, and each one has its own vibe ๐ฅฐ
<zsh-announce@zsh.org>โ announcements about releases, major changes in the shell, and the monthly posting of the Zsh FAQ ๐ฃ (moderated)<zsh-users@zsh.org>โ user discussions ๐ฌ<zsh-workers@zsh.org>โ hacking, development, bug reports and patches ๐ ๏ธ<zsh-security@zsh.org>โ private mailing list (the general public cannot subscribe to it) for discussing bug reports with security implications, i.e., potential vulnerabilities ๐
if you find a security problem in zsh itself, please mail the <zsh-security@zsh.org> address ๐จ this one is private, so it's not a public thread, bestie ๐
to subscribe or unsubscribe, send mail to the associated administrative address for the mailing list ๐จ these are the ones you'll need:
<zsh-announce-subscribe@zsh.org>๐ฅน<zsh-users-subscribe@zsh.org>๐ฅน<zsh-workers-subscribe@zsh.org>๐ฅน<zsh-announce-unsubscribe@zsh.org>๐<zsh-users-unsubscribe@zsh.org>๐<zsh-workers-unsubscribe@zsh.org>๐
YOU ONLY NEED TO JOIN ONE OF THE MAILING LISTS AS THEY ARE NESTED. all submissions to zsh-announce are automatically forwarded to zsh-users, and all submissions to zsh-users are automatically forwarded to zsh-workers ๐ช so one is literally enough, no need to join all three ๐ญ
if you have problems subscribing or unsubscribing to any of the mailing lists, send mail to <listmaster@zsh.org> ๐ซ
the mailing lists are archived, and you can get into the archives through the administrative addresses listed above ๐๏ธ there's also a hypertext archive at https://www.zsh.org/mla/ ๐ธ
๐ง THE ZSH FAQ ๐ง
zsh has a list of Frequently Asked Questions (FAQ), maintained by Peter Stephenson <pws@zsh.org> ๐โโ๏ธ it gets regularly posted to the newsgroup comp.unix.shell and the zsh-announce mailing list ๐ฎ the latest version lives at any of the Zsh FTP sites, or at https://www.zsh.org/FAQ/ โจ for FAQ-related matters, the contact address is <faqmaster@zsh.org> ๐ซถ
๐ THE ZSH WEB PAGE ๐
zsh has a web page at https://www.zsh.org/ ๐ป for web-related matters, the contact address is <webmaster@zsh.org> ๐
๐ THE ZSH USERGUIDE ๐
okay so there's a userguide in the works ๐ฅบ it's meant to complement the manual, with explanations and hints for the spots where the manual can be cabbalistic, hierographic, or downright mystifying (for example, the word hierographic does not exist ๐) you can check out its current state at https://zsh.sourceforge.io/Guide/ ๐ at the time of writing, the chapters on startup files and their contents, and on the new completion system, were essentially complete โจ
๐ INVOCATION ๐
okay so this part is about how zsh figures out where to read commands from when it starts up ๐ these flags get interpreted by the shell when it's invoked:
-cโ take the first argument as a command to execute, instead of reading commands from a script or standard input ๐ฏ if any further arguments are given, the first one gets assigned to$0, rather than being used as a positional parameter ๐-iโ force the shell to be interactive ๐ you can still specify a script to execute, though ๐ซถ-sโ force the shell to read commands from standard input ๐ฅ if-sisn't present and an argument is given, the first argument is taken to be the pathname of a script to execute ๐
so if there are any remaining arguments after option processing, and neither -c nor -s was supplied, the first argument is taken as the file name of a script full of shell commands to run ๐โโ๏ธ now here's the tea: if the option PATH_SCRIPT is set, and the file name doesn't contain a directory path (i.e. there's no / in the name), zsh searches first the current directory and then the command path given by the variable PATH for the script ๐ if the option isn't set, or the file name contains a /, it gets used directly ๐
after the first one or two arguments have been appropriated as described above, the remaining arguments get assigned to the positional parameters โจ
for further options, which are common to invocation and the set builtin, see zshoptions(1) ๐
the long option --emulate can be passed to the shell, followed (in a separate word) by an emulation mode ๐ฆ the emulation modes are the ones described for the emulate builtin, see zshbuiltins(1) ๐งฉ the --emulate option has to come before any other options (otherwise they might get overridden), but options after it are honoured, so you can use them to tweak the requested emulation mode ๐ ๏ธ also, certain extra steps are taken to make sure the emulation runs smoothly when you use this option, compared with the emulate command inside the shell ๐ฅน for example, variables that clash with POSIX usage, such as path, are not defined within the shell ๐
heads up: the --emulate option must come before any other options, or they might get overridden ๐จ
options can also be specified by name using the -o option ๐ -o acts like a single-letter option, but it takes a following string as the option name ๐ for example:
zsh -x -o shwordsplit scr
this runs the script scr, setting the XTRACE option by its corresponding letter -x, and the SH_WORD_SPLIT option by name ๐ options can be turned off by name by using +o instead of -o ๐ซถ and -o can be stacked up with preceding single-letter options, so -xo shwordsplit or -xoshwordsplit is equivalent to -x -o shwordsplit ๐ฅ
options may also be specified by name in GNU long option style, --option-name ๐ง when you do this, - characters in the option name are permitted: they get translated into _, and thus ignored ๐ซ so, for example, zsh --sh-word-split invokes zsh with the SH_WORD_SPLIT option turned on โจ like other option syntaxes, you can turn options off by replacing the initial - with a +; so +-sh-word-split is equivalent to --no-sh-word-split ๐ but here's the catch: unlike other option syntaxes, GNU-style long options cannot be stacked with any other options, so -x-shwordsplit is an error, rather than being treated like -x --shwordsplit ๐
warning: GNU-style long options can't be stacked with other options, so -x-shwordsplit is an error ๐จ
the special GNU-style option --version is handled ๐ท๏ธ it sends the shell's version information to standard output, then exits successfully ๐ --help is handled too ๐ it sends a list of options you can use when invoking the shell to standard output, then exits successfully ๐
option processing can be finished, so that following arguments starting with - or + get treated as normal arguments, and there are two ways to do it ๐ฆ firstly, a lone - (or +) as an argument by itself ends option processing ๐ secondly, there's a special option -- (or +-), which can be specified on its own (the standard POSIX usage) or stacked with preceding options (so -x- is equivalent to -x --) ๐ซถ options are not permitted to be stacked after -- (so -x-f is an error), but remember the GNU-style option form from above, where --shwordsplit is permitted and does not end option processing ๐ธ
except when the sh/ksh emulation single-letter options are in effect, the option -b (or +b) ends option processing ๐ฏ -b is like --, except that further single-letter options can be stacked after the -b and will take effect as normal ๐
๐ COMPATIBILITY ๐
okay so zsh is literally a chameleon ๐ฆ๐. If you invoke it as sh or ksh, it tries to act like that shell. It checks the first letter of the name it was called by, ignoring any leading r (that's assumed to mean restricted). If that letter is b, s or k, it switches into sh or ksh emulation. โจ
Also, if it's invoked as su (which happens on some systems when the shell gets run by the su command), zsh goes hunting for a better name in the SHELL environment variable and emulates based on that. no cap, it's so thorough ๐ญ
In sh and ksh compatibility modes, these parameters are not special and not initialized by the shell: ARGC, argv, cdpath, fignore, fpath, HISTCHARS, mailpath, MANPATH, manpath, path, prompt, PROMPT, PROMPT2, PROMPT3, PROMPT4, psvar, status. ๐
The usual zsh startup and shutdown scripts do not run in these modes. Login shells source /etc/profile first, then $HOME/.profile. If the ENV environment variable is set when the shell starts, $ENV gets sourced after the profile scripts. Its value gets parameter expansion, command substitution and arithmetic expansion before being read as a pathname. Also, the PRIVILEGED option changes how startup files get run, so keep that in mind ๐
When zsh is invoked as sh or ksh, these options get switched on: NO_BAD_PATTERN, NO_BANG_HIST, NO_BG_NICE, NO_EQUALS, NO_FUNCTION_ARGZERO, GLOB_SUBST, NO_GLOBAL_EXPORT, NO_HUP, INTERACTIVE_COMMENTS, KSH_ARRAYS, NO_MULTIOS, NO_NOMATCH, NO_NOTIFY, POSIX_BUILTINS, NO_PROMPT_PERCENT, RM_STAR_SILENT, SH_FILE_EXPANSION, SH_GLOB, SH_OPTION_LETTERS, SH_WORD_SPLIT. Extra bonus: BSD_ECHO and IGNORE_BRACES get set if zsh is invoked as sh. And if it's invoked as ksh, then KSH_OPTION_PRINT, LOCAL_OPTIONS, PROMPT_BANG, PROMPT_SUBST and SINGLE_LINE_ZLE turn on too ๐
Real talk though: zsh tries really hard to smooth over incompatibilities when they pop up, but it does not promise full emulation of other shells, and it isn't POSIX compliant either. If you want the nitty-gritty on how zsh differs from other shells, check chapter 2 of the shell FAQ at https://www.zsh.org/FAQ/ ๐๐ซถ
๐ RESTRICTED SHELL ๐
okay this one is a little spicy ๐ถ๏ธ. zsh becomes restricted when the basename of the command used to invoke it starts with the letter r, or when the -r command line option is passed at invocation. Emulation mode gets decided after the r is stripped off the invocation name. These things are disabled in restricted mode:
- changing directories with the
cdbuiltin - changing or unsetting the
EGID,EUID,GID,HISTFILE,HISTSIZE,IFS,LD_AOUT_LIBRARY_PATH,LD_AOUT_PRELOAD,LD_LIBRARY_PATH,LD_PRELOAD,MODULE_PATH,module_path,PATH,path,SHELL,UIDandUSERNAMEparameters - specifying command names that contain
/ - specifying command pathnames using
hash - redirecting output to files
- using the
execbuiltin command to replace the shell with another command - using
jobs -Zto overwrite the shell process' argument and environment space - using the
ARGV0parameter to overrideargv[0]for external commands - turning off restricted mode with
set +rorunsetopt RESTRICTED
These restrictions kick in after the startup files get processed. So the startup files should set up PATH to point at a directory of commands that are safe to run in the restricted environment. They can also add more restrictions by disabling selected builtins. โจ
You can also flip restricted mode on at any time by setting the RESTRICTED option. That instantly enables every restriction above, even if the shell hasn't finished processing all its startup files yet.
โ ๏ธ bestie, real talk: a shell Restricted Mode is an outdated way to restrict what users can do. Modern systems have better, safer and more reliable ways to lock down user actions, like chroot jails, containers and zones.
โ ๏ธ A restricted shell is super difficult to implement safely. This feature may be removed in a future version of zsh. ๐
It's really important to realise that the restrictions only apply to the shell itself, not to the commands it runs (except for some shell builtins). A restricted shell can only run the limited list of commands reachable through the predefined PATH variable, but those commands can still run any other command they like.
For example, if env is on the list of allowed commands, then the user can run any command at all, because env isn't a shell builtin and can launch arbitrary executables. ๐
So when you're building a restricted shell framework, you have to be fully aware of what each allowed command or feature (you can think of these as modules) is capable of doing.
Lots of commands can have their behaviour changed by environment variables. Apart from the few listed above, zsh doesn't restrict setting environment variables.
If a general purpose interpreted script like perl, python, bash or something similar is treated as a restricted command, the user can work around the restriction by setting specially crafted PERL5LIB, PYTHONPATH, BASHENV (etc.) environment variables. On GNU systems, any command can be made to run arbitrary code during character set conversion (zsh included) by setting a GCONV_PATH environment variable. Those are just a few examples, babe ๐
Also, bear in mind that unlike some other shells, readonly is not a security feature in zsh. It can be undone, so it can't be used to mitigate any of the stuff above.
A restricted shell only works if the allowed commands are few and carefully written so they don't hand users more access than intended. It's also important to restrict which zsh modules the user can load, because some of them, like zsh/system, zsh/mapfile and zsh/files, allow bypassing most of the restrictions. ๐จ
๐ง STARTUP/SHUTDOWN FILES ๐ง
okay so the order of startup files is kind of a whole thing ๐. Commands are read from /etc/zsh/zshenv first, and this one cannot be overridden. After that, behaviour gets shaped by the RCS and GLOBAL_RCS options. RCS affects all startup files, while GLOBAL_RCS only affects global startup files (the ones shown here with a path starting with a /). If either option gets unset at any point, any later startup file(s) of that type won't be read. A file in $ZDOTDIR can also re-enable GLOBAL_RCS. By default, both RCS and GLOBAL_RCS are set ๐
Next, commands are read from $ZDOTDIR/.zshenv. If the shell is a login shell, commands come from /etc/zsh/zprofile and then $ZDOTDIR/.zprofile. Then, if the shell is interactive, commands come from /etc/zsh/zshrc and then $ZDOTDIR/.zshrc. Finally, if the shell is a login shell, /etc/zsh/zlogin and $ZDOTDIR/.zlogin get read. ๐ฆ
When a login shell exits, $ZDOTDIR/.zlogout gets read first, followed by /etc/zsh/zlogout. This happens either with an explicit exit via the exit or logout commands, or an implicit exit by reading end-of-file from the terminal. However, if the shell terminates because it exec'd another process, the logout files are not read. These are affected by the RCS and GLOBAL_RCS options too. Also, the RCS option controls saving history files, so if RCS is unset when the shell exits, no history file gets saved. ๐
If ZDOTDIR is unset, HOME gets used instead. Files listed above as living in /etc might be in a different directory, depending on the installation. ๐
Heads up: /etc/zsh/zshenv runs for every instance of zsh, so it's super important to keep it as small as possible. It's a really good idea to wrap any code that doesn't need to run for every single shell in a test like if [[ -o rcs ]]; then ..., so it won't execute when zsh is invoked with the -f option. ๐งน
Any of these files can be pre-compiled with the zcompile builtin command (see zshbuiltins(1)). If a compiled file exists (named after the original file plus the .zwc extension) and it's newer than the original file, zsh uses the compiled one instead. โก
๐ FILES ๐
$ZDOTDIR/.zshenv
$ZDOTDIR/.zprofile
$ZDOTDIR/.zshrc
$ZDOTDIR/.zlogin
$ZDOTDIR/.zlogout
${TMPPREFIX}* (default is /tmp/zsh*)
/etc/zsh/zshenv
/etc/zsh/zprofile
/etc/zsh/zshrc
/etc/zsh/zlogin
/etc/zsh/zlogout (installation-specific - /etc is the default)
๐ SEE ALSO ๐
Go check these out, they're all iconic ๐: sh(1), csh(1), tcsh(1), rc(1), bash(1), ksh(1), zshall(1), zshbuiltins(1), zshcalsys(1), zshcompwid(1), zshcompsys(1), zshcompctl(1), zshcontrib(1), zshexpn(1), zshmisc(1), zshmodules(1), zshoptions(1), zshparam(1), zshroadmap(1), zshtcpsys(1), zshzftpsys(1), zshzle(1).
And the standard you should know about: IEEE Standard for information Technology - Portable Operating System Interface (POSIX) - Part 2: Shell and Utilities, IEEE Inc, 1993, ISBN 1-55937-255-9. ๐ค
zsh 5.9 ยท May 14, 2022 ยท ZSH(1) ๐๐โจ bestie, that's the whole page, you're officially a zsh legend now ๐ซถ๐
๐ฆ where this page comes from
- ubuntu questing: zsh 5.9-8ubuntu2
- ubuntu questing: zsh-common 5.9-8ubuntu2 ยท Zsh
- ๐ girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms