ZSH(1)

๐Ÿ“ฆ version 5.9, in ubuntu questing

๐Ÿ’– 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 ๐Ÿฆ‹

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 -s isn'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 cd builtin
  • 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, UID and USERNAME parameters
  • specifying command names that contain /
  • specifying command pathnames using hash
  • redirecting output to files
  • using the exec builtin command to replace the shell with another command
  • using jobs -Z to overwrite the shell process' argument and environment space
  • using the ARGV0 parameter to override argv[0] for external commands
  • turning off restricted mode with set +r or unsetopt 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