bash set -x: trace every command a script runs
- tool
- Bash
- command
- set
- flag
- -x
- default
- off
set -x (long form set -o xtrace) makes bash print every command to standard error after variables, globs and command substitutions are expanded, prefixed with + , right before running it. It shows what a script really executes. set +x turns it off again; bash -x script.sh traces a whole script without editing it.
What the trace looks like
cat > deploy.sh <<'EOF'
name="my app"
files=(a.txt b.txt)
set -x
echo "deploying $name"
cp "${files[@]}" /dev/null 2>/dev/null || echo "copy failed"
set +x
echo "done"
EOF
bash deploy.sh 2>&1Output
+ echo 'deploying my app' deploying my app + cp a.txt b.txt /dev/null + echo 'copy failed' copy failed + set +x done
Values appear after expansion and are quoted when they contain spaces, which makes quoting bugs visible. Nested commands (command substitution, subshells) get one extra + per level.
Add file and line numbers
PS4 is the prefix printed before each traced command (default + ). It is expanded every time, so it can show the line number and function:
cat > s.sh <<'EOF'
PS4='+ ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}: '
greet() { echo "hi $1"; }
set -x
greet world
EOF
bash s.sh 2>&1Output
+ s.sh:4:main: greet world + s.sh:2:greet: echo 'hi world' hi world
Send the trace to a file
exec 7>trace.log
BASH_XTRACEFD=7
set -xWith BASH_XTRACEFD (bash 4.1+) the trace goes to file descriptor 7 instead of standard error, so the script's own error messages stay readable.
Related options
| Option | Effect |
|---|---|
| bash -x script.sh | Trace the whole script from the first line |
| set -v | Print each input line as read, before expansion |
| bash -n script.sh | Parse only, report syntax errors, run nothing |
| set -euo pipefail | Stop on errors, unset variables and failed pipelines |