There are two ways to specify debugging options.
One is to specify options when executing the <span>bash</span> command:
# -x prints commands and their arguments during execution
bash -x debug.sh
The other is to set it in the <span>bash</span> script using the built-in command <span>set</span>:
#!/bin/bash
set -x # Enable debugging
# Here is some code
set +x # Disable debugging
Generally, the following options are used to debug scripts.
| Option | Description |
|---|---|
| -x | Debug mode: displays each command executed |
| -v | Verbose mode: displays each line of the script |
| -n | Syntax check: checks without executing |
| -u | Strict mode: reports an error for undefined variables |
| -e | Fail fast: exits immediately if a command fails |
| -o pipefail | Pipe fail: exits if any command in a pipeline fails |
1. Debugging Options
First, prepare a script called <span>debug.sh</span>:
#!/usr/bin/env bash
radius=2.5
result=$(awk -v r=$radius 'BEGIN {printf "%.3f", 3.14159 * r * r}')
echo "area: $result"
<span>-x: Debug mode: displays each command executed</span>
bash-5.3$ bash -v debug.sh
+ radius=2.5
++ awk -v r=2.5 'BEGIN {printf "%.3f", 3.14159 * r * r}'
+ result=19.635
+ echo 'area: 19.635'
area: 19.635
<span>-v: Verbose mode: displays each line of the script</span>
bash-5.3$ bash -v debug.sh
#!/usr/bin/env bash
radius=2.5
result=$(awk -v r=$radius 'BEGIN {printf "%.3f", 3.14159 * r * r}')
echo "area: $result"
area: 19.635
<span>-xv: Combination of debug mode and verbose mode</span>
#!/usr/bin/env bash
radius=2.5
+ radius=2.5
result=$(awk -v r=$radius 'BEGIN {printf "%.3f", 3.14159 * r * r}')
++ awk -v r=2.5 'BEGIN {printf "%.3f", 3.14159 * r * r}'
+ result=19.635
echo "area: $result"
+ echo 'area: 19.635'
area: 19.635
From the above, we can see:<span>-v</span>: displays each line of the script followed by <span>-x</span>: displays each command executed. We can also see the result of parameter expansion for <span>r=2.5</span>.
Let’s also debug the <span>glob</span> pattern:
bash-5.3$ set -xv
bash-5.3$ ls -l *
ls -l *
+ ls -l log1.txt log2.txt log3.txt
-rw-r--r-- 1 user staff 19 11 4 16:01 log1.txt
-rw-r--r-- 1 user staff 0 11 4 12:40 log2.txt
-rw-r--r-- 1 user staff 0 11 4 12:40 log3.txt
bash-5.3$ set +xv
<span>-n: Syntax check: checks without executing</span>
# No syntax issues
bash-5.3$ bash -n debug.sh
# No output
Now change <span>radius=2.5</span> to <span>radius = 2.5</span>, adding spaces around the equal sign.
#!/usr/bin/env bash
radius = 2.5
result=$(awk -v r=$radius 'BEGIN {printf "%.3f", 3.14159 * r * r}')
echo "area: $result"
area: 19.635
Execute again:
bash-5.3$ bash -n debug.sh
# what? No output.
# The author is confused again......
Here, the author indeed did not detect the <span>radius = 2.5</span> syntax error with spaces around the equal sign using <span>-n</span>. Is this a runtime error?
The actual execution will result in an error:
./debug.sh: line 2: radius: command not found
area: 0.000
Add an erroneous <span>if</span> statement:
#!/usr/bin/env bash
if [ "$1" = "2" ] ; then
echo 'hello'
radius = 2.5
result=$(awk -v r=${radius} 'BEGIN {printf "%.3f", 3.14159 * r * r}')
echo "area: $result"
This time it was detected:
bash-5.3$ bash -n debug.sh
debug.sh: line 10: syntax error: unexpected end of file in "if" command on line 3
<span>-e: Fail fast: exits immediately if a command fails</span>
#!/usr/bin/env bash
# strict_e.sh
echo "Starting"
# This will cause the script to exit because the directory may not exist
ls /nonexistent_directory
echo "This line will not execute"
<span>Result:</span>
bash-5.3$ bash strict_e.sh
Starting
ls: /nonexistent_directory: No such file or directory
This line will not execute
bash-5.3$ bash -e strict_e.sh
Starting
ls: /nonexistent_directory: No such file or directory
<span>-u: Strict mode: reports an error for undefined variables</span>
#!/usr/bin/env bash
# strict_u.sh
# Using an undefined variable
undefined_variable=$NONEXISTENT_VAR
echo "This line will not execute"
<span>Result:</span>
bash-5.3$ bash strict_u.sh
This line will not execute
bash-5.3$ bash -u strict_u.sh
strict_u.sh: line 4: NONEXISTENT_VAR: unbound variable
<span>-o pipefail: Pipe fail: exits if any command in a pipeline fails</span>
<span>-e</span>has an exception, as it does not apply to pipeline commands.
#!/usr/bin/env bash
# pipefail.sh
unknown_cmd | echo a
echo hello
<span>Run it:</span>
bash-5.3$ bash pipefail.sh
pipefail.sh: line 4: unknown_cmd: command not found
a
hello
bash-5.3$ bash -e pipefail.sh
pipefail.sh: line 4: unknown_cmd: command not found
a
hello
From the execution results, we can see that <span>unknown_cmd | echo a</span> is successful, so the subsequent code continues to execute.
bash-5.3$ bash -o pipefail pipefail.sh
pipefail.sh: line 4: unknown_cmd: command not found
a
hello
bash-5.3$ bash -eo pipefail pipefail.sh
pipefail.sh: line 4: unknown_cmd: command not found
a
<span>-eo pipefail</span> must be specified together to exit on <span>pipe fail</span>, compensating for the exception of <span>-e</span>.
2. Summary
Generally, options are used together:
bash -euxo pipefail script.sh
Additionally, options can also be specified in the script file using <span>set</span>:
# Method 1
set -euxo pipefail
# Method 2
set -eux
set -o pipefail
Finally, as a good practice, it is recommended to specify <span>set -euo pipefail</span> in each script file.
#!/usr/bin/env bash
set -euo pipefail
# Following is functional code