Zsh script failures are easier to isolate when the parse check, normal output, and trace log are treated as separate evidence. A syntax check catches parse errors before any branch runs, a normal run confirms the result the caller sees, and execution tracing shows the commands that produced that result.
Use zsh -n to parse a file without executing it, then use zsh -x when the script still needs command-by-command evidence. A custom PS4 value that includes %N and %i adds the script or function name and line number to trace lines, which makes a failing branch easier to connect to the source file.
The example runs Zsh with -f while tracing so user startup files do not add aliases or functions to the run. A system /etc/zshenv file can still appear at the top of the trace on some packages, so keep trace logs local until arguments, paths, tokens, hostnames, and environment values have been reviewed.
Related: How to create a function in Zsh
Related: How to compile a Zsh script
Related: How to configure Zsh startup files
Related: Debug a Bash script
#!/usr/bin/env zsh
emulate -L zsh
setopt err_exit
PS4='+ %N:%i: '
show_item() {
local name=${1:-sample}
print -r -- "item=$name"
}
show_item "${1:-sample}"
The custom PS4 prefix is assigned before the function call so later trace lines show the call site and the lines executed inside show_item.
$ zsh -n debug-demo.zsh
No output from zsh -n means Zsh parsed the file without finding a syntax error.
$ zsh -f debug-demo.zsh report item=report
-f starts Zsh with normal user startup files skipped. A packaged /etc/zshenv file can still run because Zsh reads that system file for every shell.
Related: How to configure Zsh startup files
$ zsh -fx debug-demo.zsh report 2>trace.log item=report
Trace output goes to standard error, so 2>trace.log keeps it separate from normal standard output.
$ cat trace.log ##### snipped +debug-demo.zsh:2> emulate -L zsh +debug-demo.zsh:3> setopt err_exit +debug-demo.zsh:5> PS4='+ %N:%i: ' + debug-demo.zsh:12: show_item report + show_item:1: local name=report + show_item:2: print -r -- 'item=report'
The snipped line represents package-specific /etc/zshenv startup trace output. The remaining lines show the script entry, the function call site, and the commands executed inside show_item.
$ rm trace.log debug-demo.zsh
Do not commit trace logs or paste them into tickets until sensitive values have been removed.