Trace with line numbers
Add the file name, line number and function to every set -x line by setting PS4 first. The default plus sign prefix gives no hint where a command came from.
set -x prints each command, but in a long script with includes you cannot tell which file or line a trace line belongs to.
cat > demo.sh <<'EOF'
#!/bin/bash
PS4='+ ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}: '
set -x
greet() {
local name=$1
echo "hello $name"
}
greet world
greet bash
EOF
bash demo.sh+ demo.sh:8:main: greet world
+ demo.sh:5:greet: local name=world
+ demo.sh:6:greet: echo 'hello world'
hello world
+ demo.sh:9:main: greet bash
+ demo.sh:5:greet: local name=bash
+ demo.sh:6:greet: echo 'hello bash'
hello bashBash expands PS4 before each traced command, and the prefix is printed first, repeated once per level of nesting. The manual lists the variables a prompt can use under shell variables. BASH_SOURCE is the file, LINENO the line and FUNCNAME the function, so each trace line now says where it came from. The local assignment and the echo show the function name in the third field.
Single quotes matter: with double quotes the variables expand once when PS4 is set, and every line prints the same location. Set PS4 inside the script, before set -x. Bash ignores a PS4 inherited from the environment when it runs as root, so PS4=... bash -x script printed no location in the run here, although it works for a normal user. zsh uses PS4='%N:%i> ', and has no FUNCNAME.