Run a script in trace mode without editing it
Trace a script from outside with bash -x script.sh, and add SHELLOPTS=xtrace to trace the scripts it calls as well, which plain bash -x leaves silent.
A script you cannot or will not edit misbehaves, and you want to see what it runs.
cat > child.sh <<'EOF'
#!/bin/bash
echo "child: $1"
EOF
cat > parent.sh <<'EOF'
#!/bin/bash
name=world
echo "parent: $name"
bash child.sh "$name"
EOF
bash -x parent.sh
echo ---
env SHELLOPTS=xtrace bash parent.sh+ name=world
+ echo 'parent: world'
parent: world
+ bash child.sh world
child: world
---
+ name=world
+ echo 'parent: world'
parent: world
+ bash child.sh world
+ echo 'child: world'
child: worldStarting the interpreter with -x has the same effect as set -x on the first line, so nothing in the file changes. The option is not passed on to other scripts that the first one starts, which is why the child is silent in the first run. SHELLOPTS is a variable that Bash exports and reads at startup, so with xtrace in it every Bash process in the tree traces, as the Bash manual explains.
This works for Bash scripts only, since sh scripts ignore SHELLOPTS unless they run under Bash. The variable is read-only inside a running shell, which is why the second command sets it through env and not as a plain prefix. When the script is executed directly with a shebang, bash -x ./script still works but the shebang line is not honoured, so pass the right interpreter. A script that calls itself or a tool with a lot of output can produce a huge trace; send it to a file with 2> trace.log.