Skip to content

/flags/bash/set-x-xtrace

bash set -x: trace every command a script runs

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
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

bash
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>&1

Output

+ 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:

bash
cat > s.sh <<'EOF'
PS4='+ ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}: '
greet() { echo "hi $1"; }
set -x
greet world
EOF
bash s.sh 2>&1

Output

+ s.sh:4:main: greet world
+ s.sh:2:greet: echo 'hi world'
hi world

Send the trace to a file

bash
exec 7>trace.log
BASH_XTRACEFD=7
set -x

With 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.

OptionEffect
bash -x script.shTrace the whole script from the first line
set -vPrint each input line as read, before expansion
bash -n script.shParse only, report syntax errors, run nothing
set -euo pipefailStop on errors, unset variables and failed pipelines

Sources