Tools#
The toolchain for zsh scripts is mostly POSIX utilities plus a
few zsh-specific helpers. zsh itself ships zprof for
profiling and zmodload for runtime module control;
third-party tooling adds linting (with caveats) and formatting.
Interpreter#
zsh, the Z shell.sh, POSIX shell (often dash, ash, or busybox sh on minimal images).
Check the version.
$ zsh --version
Useful invocation flags.
Flag |
Effect |
|---|---|
|
Trace each command before it runs (same as |
|
Parse but do not execute (syntax check) |
|
Skip |
|
Skip all init files |
|
Run the command string |
$ zsh -n script.zsh # syntax check
$ zsh -f # clean interactive shell
$ zsh -xc 'cmd' # trace a one-liner
Linting#
ShellCheck, the standard shell linter. Limited zsh support. ShellCheck targets bash / sh / dash / ksh; running it on a zsh script catches the bash-shared bugs but misses zsh-specific surface (glob qualifiers, parameter-expansion flags,
zparseopts,setoptnames).
$ shellcheck --shell=bash script.zsh
zsh -n, syntax check only. Catches parser errors, not semantic bugs.
$ zsh -n script.zsh
Formatting#
shfmt, formatter for sh / bash / mksh. No native zsh dialect; pass
-ln bashand expect some hand-fix on zsh-specific constructs (glob qualifiers,=()process substitution,foreachform).
$ shfmt -i 2 -ci -ln bash -w script.zsh
Profiling#
zsh/zprof, ships with zsh; profile startup and function calls.
$ # at the top of ~/.zshrc
$ zmodload zsh/zprof
$ # at the bottom
$ zprof | head -20
Reads the table top-down. The largest entry at the top is the
biggest share of the startup budget; mitigations include
autoload, zinit turbo-mode, and trimming
compinit -d to a cached digest.
For per-call timing of a function, time works.
$ time my_function arg1 arg2
Testing#
zunit, TAP-style test framework written for zsh.
bats-core, TAP style; runs zsh scripts too.
shunit2, xUnit-style assertions; portable across POSIX shells.
$ #!/usr/bin/env zunit
$ @test 'addition works' {
$ result=$(( 2 + 3 ))
$ assert "$result" equals 5
$ }
Debugging#
set -x, trace each command before it runs.set -e, exit on the first failed command.set -u, error on unset variables.zsh -x, trace mode without modifying the script.functrace/PS4, customise the trace prefix to include line numbers and function names.
A useful trace prefix.
$ PS4='+%N:%i> ' # function-name:lineno
$ set -x
zshdb, gdb-style debugger for zsh.
Editing#
Most editors recognise zsh by shebang or extension. Useful add-ons.
bash-language-server, LSP backed by ShellCheck. Recognises
#!/usr/bin/env zshbut lints under bash rules.zsh-mode (Emacs), vim-zsh (Vim), Shell-syntax (VS Code), syntax highlighting and indentation.
Cross-Platform#
For scripts that target multiple shells, validate against POSIX with the tools the POSIX neighbors ship.
$ shellcheck -s sh script.sh
$ shfmt -ln posix -d script.sh
For zsh-only features that the operator does want to lock in, declare the shebang and stop worrying about portability.
References#
Patterns for the strict-mode / profiling discipline these tools support.
Libraries for
zmodload, autoload, and the built-in module catalog.man 1 zsh,man 1 zshoptions,man 1 zshbuiltins.ShellCheck (lint).