Last updated:

It may seem straight forward to debug scripts in Bash when writing simple and short shell scripts. Though, the complexity tends to increase when maintaining a larger code base or when collaborating with your peers. In such cases, following consistent steps to debug scripts is critical to ensure a quick debugging process.

This post covers various special properties of the Bash Shell and how you can use them to efficiently debug scripts.

What Are The 3 Types Of Scripting Errors

Debugging a Bash shell script may be tedious at times. By following the 5 simple steps in this post you will prevent yourself from some major headaches.

There are three basic types of errors when programming and it remains true in shell scripts.

  1. Syntax Error
    Usually, Syntax Errors are the easiest errors to solve in a Bash script. They can often be found without executing the shell script. The most common syntax errors include:

  2. Runtime Error
    A Runtime Error will be the next level of errors. The shell script runs with no syntax errors but fails to execute reliably certain tasks. The most common runtime errors in a shell script include:

  3. Logic Error
    Logic Errors are often the most difficult to troubleshoot as it relates to design flaws and logic mistakes. Debugging such errors can be difficult unless having a thorough understanding of the end-to-end execution of the script and the expected behavior. The most common logic errors in a shell script include:

    • Incorrect use of a test operator in a Conditional Statement like using -z instead of -n in a if condition
    • Incorrect use of an Arithmetic operator like multiplying instead of diving a number
    • Incorrect condition to exit from a Bash Loop like when using a while loop instead of an until loop
    • Incorrectly manipulating Linux dates with different timezones
    • Showing an incorrect message to the shell prompt or in logs

The 5 Steps To Debug a Script in Bash

Step 1: Use a Consistent Debug Library

Whatever is the purpose of your shell scripts, whether you work solo or collaborating with teammates, it is essential to be consistent and systematic in your debugging process to troubleshoot and debug a shell script quickly.

You can systematically source a shared library that contains all your debugging settings by using the source and dot commands but this approach is not ideal if you inherited someone else problem and don’t want or cannot change the original code.

An alternative is to use the $BASH_ENV environment variable which is used by Bash to define an init file to read before executing a script. You can leverage it to create an environment for debugging purposes and define specific shell options or debugging traps.

The $ENV variable is similar to the $BASH_ENV. It is used when the shell runs in POSIX compatibility mode.

### Define Debug environment
### Filename: my-debug-env
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
### Example Script
### Filename: example-debug

echo "Example Script..."
bad_command &> /dev/null

### Example output with no debug env
[me@linux ~]$ ./example-debug 
Example Script...

### Example output with the debug env
[me@linux ~]$ BASH_ENV=./my-debug-env ./example-debug
Example Script...
bad_command &> /dev/null failed with error code 127

In the next steps, we will keep enhancing the content of the BASH_ENV init script ./my-debug-env. The benefit of this technique is that no matter what script you are going to debug and which Linux server you are on, you will have consistent settings, traps, and logging (unless overridden by the script itself).

Step 2: Check For Syntax Error

Finding a syntax error only after executing a shell script can be frustrating and this is not ideal in a case where the command executed takes a long time to run or create a lot of other artifacts.

Let’s take a simple but common syntax error, with the incorrect use of the single square bracket and a missing then keyword.

#!/usr/bin/env bash
# Filename: ./example-syntax-error
echo "This got executed"
if [-z "$v" ]; 
 echo "There is a typo"
fi

## Output
[me@linux ~]$ ./example-syntax-error
This got executed
./test: line 12: syntax error near unexpected token `fi'
./test: line 12: `fi'

There is two options in Bash to catch this kind of syntax errors without executing the script. First, by using the shell noexec option. Second, by using a static analysis tool.

Use The noexec Shell Option

The Bash shell provides the noexec shell option which can be set either with set -o noexec or using set -n. This option will make the Bash shell only read the commands but will not execute them, neither will it do a variable assignment. In short, this is a basic “no-op” or “dry run” mode.

⚠️ Once the noexec option is executed by the set builtin command, any other commands after that will NOT be executed, this include any following set command.

You can easily include a small condition and the use of an environment variable to automatically enable the noexec option to check the syntax of your script. This can be done directly in your script source, or better in your debug environment init script my-debug-env as mentioned in step 1 by using the BASH_ENV variable. This can be convenient in a CI/CD pipeline before deploying your script in production.

### Define Debug environment
### Filename: my-debug-env
if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
# Filename: example-syntax-error
echo "This got executed"
if [-z "$v" ]; 
 echo "There is a typo"
fi

## Output
[me@linux ~]$ NOOP=1 BASH_ENV=my-debug-env ./example-syntax-error
Run NOOP mode
./example-syntax-error: line 6: syntax error near unexpected token `fi'
./example-syntax-error: line 6: `fi'

Now, you can get the shell syntax error exposed without executing any of the commands in the script.

👉 This method will not be able to catch syntax errors in a shell script imported with the source or dot commands since the commands will not be executed in the noexec mode. Each script should be tested individually for syntax errors.

Use A Static Analysis Tool: ShellCheck

One of the problems with the noexec mode is that it will just output the raw syntax errors which are not always easy to interpret and diagnose. A better approach is to use a static analysis tool like ShellCheck which has become a defacto standard when it comes to Bash scripts static analysis.

With our previous syntax error example, shellcheck provides us with much more friendly information and helpful recommendations on how to fix the errors.

[me@linux ~]$ shellcheck ./example-syntax-error 

In ./example-syntax-error line 9:
if [-z "$v" ]; 
^-- SC1049: Did you forget the 'then' for this 'if'?
^-- SC1073: Couldn't parse this if expression. Fix to allow more checks.
   ^-- SC1035: You need a space after the [ and before the ].


In ./example-syntax-error line 11:
fi
^-- SC1050: Expected 'then'.
  ^-- SC1072: Unexpected keyword/token. Fix any mentioned problems and try again.

For more information:
  https://www.shellcheck.net/wiki/SC1035 -- You need a space after the [ and ...
  https://www.shellcheck.net/wiki/SC1049 -- Did you forget the 'then' for thi...
  https://www.shellcheck.net/wiki/SC1050 -- Expected 'then'.

Once we fix those obvious syntax errors, shellcheck will go a step further and even provide notices on variables not properly assigned which allow us to prevent some possible runtime errors.

[me@linux ~]$ shellcheck ./example-syntax-error 

In example-xtrace line 9:
if [[ -z "$v" ]]; then
          ^-- SC2154: v is referenced but not assigned.

For more information:
  https://www.shellcheck.net/wiki/SC2154 -- v is referenced but not assigned.

👉 There is a lot that you can do with shellcheck to reduce the number of errors in your bash shell scripts and help you debug quickly. I will generally use explicitly the following syntax shellcheck -s bash -o all -xa <your_script> to get the benefit of all the possible recommendations (option -o all) and ensure sourced files are also checked (options -xa).

Step 3: Trace Your Script Command Execution

Another helpful shell option to use is the xtrace option that can be enabled with set -o xtrace or set -x. When your Bash script runs in trace mode all the commands and their arguments get printed out before being executed. This is helpful to debug runtime and logic errors in a script.

📎 You can replace the default + character used in the xtrace output by changing the Bash Prompt $PS4 Variable.

Alternatively, you can use the shell verbose option instead by using set -o verbose or set -v. The main difference with xtrace is that the verbose mode will only display the shell input lines as they are read. You will not see the arguments passed to the commands which is usually helpful when trying to debug a bash script. You may find useful to use both.

### Define Debug environment
### Filename: my-debug-env
if [[ -v TRACE ]]; then
  echo "Run TRACE mode"
  set -o xtrace # same as set -x
fi
if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
# Filename: ./example-xtrace
echo "This got executed"
v=$1
if [[ -z "${v}" ]]; then
 echo "\$v is empty"
fi

## Output
[me@linux ~]$ NOOP=1 TRACE=1 BASH_ENV=my-debug-env ./example-xtrace 
Run TRACE mode
+ [[ -v NOOP ]]
+ echo 'Run NOOP mode'
Run NOOP mode
+ set -o noexec

[me@linux ~]$ TRACE=1 BASH_ENV=my-debug-env ./example-xtrace 
Run TRACE mode
+ [[ -v NOOP ]]
+ echo 'This got executed'
This got executed
+ [[ -z '' ]]
+ echo '$v is empty'
$v is empty

This kind of tracing output is fine but it can quickly make it harder to debug a script as all the script output gets mixed up on the terminal. The simple and obvious solution is to redirect the standard errors to a different file by using for example TRACE=1 ./example-xtrace 2>error.log. Though, if your script makes use of the standard error output, then you still have the debug trace mixed with standard errors.

To solve this problem, Bash can write the trace output generated by the xtrace shell option to a given file descriptor as specified by the $BASH_XTRACEFD variable. This allows the tracing output to be separated from the script standard output and standard error messages. Our following example use a file xtrace.out but you can instead use a shell redirection to a logging utility like logger or syslog.

The file descriptor is closed when the $BASH_XTRACEFD variable is unset or assigned a new value. Unsetting $BASH_XTRACEFD or assigning it the empty string causes the trace output to be sent to the standard error.

⚠️ Setting $BASH_XTRACEFD to the standard error file descriptor (2) and then unsetting it will result in the standard error being closed.

[me@linux ~]$ lsof -p $$ # List all current open files
...
[me@linux ~]$ ls /proc/$$/fd # Use /dev/fd/ on mac
0 1 2 255
[me@linux ~]$ exec 4>xtrace.out # Could be replaced by: exec 4> >(logger -t $0)
[me@linux ~]$ ls /proc/$$/fd
0 1 2 255 4
[me@linux ~]$ BASH_XTRACEFD=4
[me@linux ~]$ set -x
[me@linux ~]$ less xtrace.out
++ update_terminal_cwd
++ local url_path=
++ local i ch hexch LC_CTYPE=C LC_ALL=
++ (( i = 0 ))
...
[me@linux ~]$ set +x
[me@linux ~]$ unset BASH_XTRACEFD
[me@linux ~]$ ls /proc/$$/fd
0 1 2 255

Our previous example can then be updated to make use of the BASH_XTRACEFD variable when tracing is enabled.

### Define Debug environment
### Filename: my-debug-env
if [[ -v TRACE ]]; then
  echo "Run TRACE mode"
  exec 4>./xtrace.out # Could be replaced by: exec 4> >(logger -t $0)
  BASH_XTRACEFD=4
  set -o xtrace # same as set -x
fi
if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi
trap 'echo "$BASH_COMMAND" failed with error code $?' ERR

#!/usr/bin/env bash
# Filename: ./example-xtrace
echo "This got executed"
v=$1
if [[ -z "${v}" ]]; then
 echo "\$v is empty"
fi

## Output
[me@linux ~]$ TRACE=1 BASH_ENV=my-debug-env ./example-xtrace 
Run TRACE mode
This got executed
$v is empty
[me@linux ~]$ cat xtrace.out 
+ [[ -v NOOP ]]
+ echo 'This got executed'
+ v=
+ [[ -z '' ]]
+ echo '$v is empty'

Step 4: Use The Extended Debug Mode

Using a debugger is often necessary to understand runtime or logic errors in a software program, this can also be true with a large shell script.

📎 The need for a debugger may also be a major red flag that you are writing in the wrong language. Bash is not always a good fit for every job. Bash scripts should stay simple and straight forward.

The Bash shell can run in an extended debug mode by using the shell extdebug option which enables the interactive Bash debugger to investigate and debug your script step by step.

When using shopt -s extdebug, the shell will also automatically enable the set builtin option errtrace and functrace for error tracing in bash functions. With the errtrace and functrace options, you get access to a DEBUG trap, a RETURN trap, and the Bash special variables BASH_ARGC, BASH_ARGV, and BASH_ARGV0.

⚠️ The extdebug option depends on the bashdb utility. If it’s not installed you will get the error warning: cannot start debugger; debugging mode disabled. The bashdb command may not be available on your platform and may require a manual install. On Ubuntu 20.04, you can use this bashdb PPA and on Mac you can use brew install bashdb. If you are using VS codium or VS code, you can use the vscode-bash-debug extension which includes bashdb.

Additionally to the extdebug option, you can call the bash debugger directly from the command line using one of the command below:

bashdb [options] [--] script-name [script options]
bashdb [options] -c execution-string
bash --debugger [bash-options...] script-name [script options]

👉 If you don’t want or cannot use the bashdb debugger, you can replace the use of shopt -s extdebug to the shell debug/trace options with set -o errtrace and set -o functrace which provide most of the functionality discussed below, except for the interactive step-by-step debugger.

To handle the debugger mode in our current example, you can add a small block to the my-debug-env startup script which will set the shell options as necessary depending on a custom environment variable DEBUGGER to be set or not.

if [[ -v DEBUGGER ]]; then
  shopt -s extdebug
else
  set -o errtrace
  set -o functrace
fi

It’s important to have the errtrace and functrace options enable one way or another as it will make sure the ERR, DEBUG, RETURN traps are inherited by the command substitution, shell functions, and subshells.

[me@linux ~]$ trap 'echo "ERR trap from ${FUNCNAME:-MAIN} context."' ERR
[me@linux ~]$ false
ERR trap from MAIN context.
[me@linux ~]$ fn() { false; }; fn ; echo "fn exit code is $?"
ERR trap from MAIN context.
fn exit code is 1
[me@linux ~]$ fn() { false; true; }; fn ; echo "fn exit code is $?"
fn exit code is 0
[me@linux ~]$ set -o errtrace
[me@linux ~]$ fn() { false; true; }; fn ; echo "fn exit code is $?"
ERR trap from fn context.
fn exit code is 0

The most common command inside the Bash debugger, bashdb, will be the set linetrace on to print every command executed, print var to display the current assigned value of the variable var, the step n (or s n) to get to the next action by n step (n=1 if not provided) and cont (or c) to continue running the full script.

[me@linux ~]$ DEBUGGER=1 BASH_ENV=my-debug-env ./example-debug param1
bash debugger, bashdb, release 5.0-1.1.2

Copyright 2002-2004, 2006-2012, 2014, 2016-2019 Rocky Bernstein
This is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.

(/home/jdoe/example-debug:3):
3:      echo $BASH_ENV
bashdb<0> set linetrace on
bashdb<1> step
my-debug-env
(/home/jdoe/example-debug:4):
4:      set -o
bashdb<2> cont
(/home/jdoe/example-debug:16):
level 1, subshell 0, depth 0:   echo "Example Script..."
Example Script...
(/home/jdoe/example-debug:17):
level 1, subshell 0, depth 0:   echo "Main BASH_ARGC=${BASH_ARGC[@]} BASH_ARGV=${BASH_ARGV[@]}"
Main BASH_ARGC=1 BASH_ARGV=param1
...

The $BASH_ARGC and $BASH_ARGV variables are arrays containing information about the script and functions positional parameters. $BASH_ARGC contains the number of parameters in each frame of the current bash execution call stack ordered from the last to the first call, while $BASH_ARGV contains all of the parameters in the current bash execution call stack ordered from the last to first call.

### Example Script
#!/usr/bin/env bash
shopt -s extdebug
debug() {
 echo "Func: ${BASH_ARGC[@]} ${BASH_ARGV[@]}"
}

echo "Main: ${BASH_ARGC[@]} ${BASH_ARGV[@]}"
debug c

### Example output
[me@linux ~]$ ./example-argdebug a b
Main: 2 b a
Func: 1 2 c b a

The $BASH_ARGV0 is identical to the special parameter $0 and expands to the name of current the shell or shell script. Assigning a new value to $BASH_ARGV0 will also update the $0 special parameter. $BASH_ARGV0 will lose its special properties when being unset. This variable is available since Bash version 5.

[me@linux ~]$ echo $0 $BASH_ARGV0
/usr/local/bin/bash /usr/local/bin/bash

Step 5: Provide Meaningful Debug Logs

Finally, similar to how important it is to write quality bash comments, it is as important to have meaningful debug logs and there are a few bash environment variables that you may want to leverage to debug your script quickly.

The $BASH_COMMAND contains the name of the command currently being executed or about to be executed, except when the shell is executing a command as the result of a trap, then the value is set to the command executed at the time of the trap. This variable is mostly useful for debug logs or error logs.

[me@linux ~]$ trap 'echo "$BASH_COMMAND" failed with error code $?' ERR
[me@linux ~]$ bad_command &>/dev/null
bad_command &> /dev/null failed with error code 127

The $BASH_LINENO and $BASH_SOURCE are bash array variables. Those variables are often used for debugging and logging purposes and works in conjunction with the bash function variable $FUNCNAME. The $BASH_SOURCE variable contains the source filenames where the corresponding shell function names in the $FUNCNAME array variable are defined. The $BASH_LINENO variable contains the line numbers in source files where each corresponding member of $FUNCNAME was invoked.

The $LINENO contains the line number in the script or shell function currently executing.

### Example script
### Filename: example-debug
#!/usr/bin/env bash

debug() {
 echo "Func BASH_SOURCE: ${!BASH_SOURCE[@]} ${BASH_SOURCE[@]}"
 echo "Func BASH_LINENO: ${!BASH_LINENO[@]} ${BASH_LINENO[@]}"
 echo "Func FUNCNAME: ${!FUNCNAME[@]} ${FUNCNAME[@]}"
 echo "Func LINENO: ${LINENO}"
}
echo "Example Script..."
echo "Main BASH_SOURCE: ${!BASH_SOURCE[@]} ${BASH_SOURCE[@]}"
echo "Main BASH_LINENO: ${!BASH_LINENO[@]} ${BASH_LINENO[@]}"
echo "Main FUNCNAME: ${!FUNCNAME[@]} ${FUNCNAME[@]}"
echo "Main LINENO: ${LINENO}"
debug

### Example output
[me@linux ~]$ ./example-debug 
Example Script...
Main BASH_SOURCE: 0 ./example-debug
Main BASH_LINENO: 0 0
Main FUNCNAME:
Main LINENO: 13
Func BASH_SOURCE: 0 1 ./example-debug ./example-debug
Func BASH_LINENO: 0 1 12 0
Func FUNCNAME: 0 1 debug main
Func LINENO: 7

Some other shell variables that may be worth using include the BASHPID, PID, BASH, SHELL, BASHOPTS, SHELLOPTS, POSIXLY_CORRECT, and BASH_COMPAT. Those variables may be helpful to print at the beginning or end of the log ouput to know what is the current environment in which your script is running. Often, you may assume some shell option to be enabled or find out that you are running in a compatibility mode that disables some features you expect to have.

A Complete Example

Based on those detailed steps, we can produce a simple Bash debug environment init script to be used whenever we want to debug or profile a bash script.

👉 Feel free to change the below example to include the level of debugging information that you want so it can fit your needs.

### Define Debug environment
### Filename: my-debug-env

PS4='+[$0:$LINENO] '

if [[ -v DEBUGGER ]]; then
  shopt -s extdebug
else
  set -o errtrace
  set -o functrace
fi

if [[ -v TRACE ]]; then
  echo "Run TRACE mode"
  exec 4>./xtrace.out
  BASH_XTRACEFD=4
  set -o xtrace # same as set -x
fi

if [[ -v NOOP ]]; then
  echo "Run NOOP mode"
  set -o noexec # same as set -n
fi

debug() {
 echo "[ DEBUG ]| BASH_COMMAND=${BASH_COMMAND}"
 echo "         | BASH_ARGC=${BASH_ARGC[@]} BASH_ARGV=${BASH_ARGV[@]}"
 echo "         | BASH_SOURCE: ${!BASH_SOURCE[@]} ${BASH_SOURCE[@]}"
 echo "         | BASH_LINENO: ${!BASH_LINENO[@]} ${BASH_LINENO[@]}"
 echo "         | FUNCNAME: ${!FUNCNAME[@]} ${FUNCNAME[@]}"
}

trap 'echo ERR trap from ${FUNCNAME:-MAIN} context. $BASH_COMMAND failed with error code $?' ERR
trap 'debug' DEBUG

We can give it a try with our simple example script.

#!/usr/bin/env bash
# Filename: ./example-xtrace
echo "This got executed"
v=$1
if [[ -z "${v}" ]]; then
 echo "\$v is empty"
fi

## Debug Output
[me@linux ~]$ TRACE=1 BASH_ENV=my-debug-env ./example-xtrace param1
Run TRACE mode
[ DEBUG ]| BASH_COMMAND=echo "This got executed"
         | BASH_ARGC=1 BASH_ARGV=param1
         | BASH_SOURCE: 0 1 my-debug-env ./example-xtrace
         | BASH_LINENO: 0 1 3 0
         | FUNCNAME: 0 1 debug main
This got executed
[ DEBUG ]| BASH_COMMAND=v=$1
         | BASH_ARGC=1 BASH_ARGV=param1
         | BASH_SOURCE: 0 1 my-debug-env ./example-xtrace
         | BASH_LINENO: 0 1 4 0
         | FUNCNAME: 0 1 debug main
[ DEBUG ]| BASH_COMMAND=[[ -z "${v}" ]]
         | BASH_ARGC=1 BASH_ARGV=param1
         | BASH_SOURCE: 0 1 my-debug-env ./example-xtrace
         | BASH_LINENO: 0 1 5 0
         | FUNCNAME: 0 1 debug main

# XTRACE Logs
[me@linux ~]$ cat xtrace.out 
+[bash:20] [[ -v NOOP ]]
+[bash:33] trap 'echo ERR trap from ${FUNCNAME:-MAIN} context. $BASH_COMMAND failed with error code $?' ERR
+[bash:34] trap debug DEBUG
++[./example-xtrace:3] debug
++[./example-xtrace:26] echo '[ DEBUG ]| BASH_COMMAND=echo "This got executed"'
++[./example-xtrace:27] echo '         | BASH_ARGC=1 BASH_ARGV=param1'
++[./example-xtrace:28] echo '         | BASH_SOURCE: 0' '1 my-debug-env' ./example-xtrace
++[./example-xtrace:29] echo '         | BASH_LINENO: 0' '1 3' 0
++[./example-xtrace:30] echo '         | FUNCNAME: 0' '1 debug' main
+[./example-xtrace:3] echo 'This got executed'
++[./example-xtrace:4] debug
++[./example-xtrace:26] echo '[ DEBUG ]| BASH_COMMAND=v=$1'
++[./example-xtrace:27] echo '         | BASH_ARGC=1 BASH_ARGV=param1'
++[./example-xtrace:28] echo '         | BASH_SOURCE: 0' '1 my-debug-env' ./example-xtrace
++[./example-xtrace:29] echo '         | BASH_LINENO: 0' '1 4' 0
++[./example-xtrace:30] echo '         | FUNCNAME: 0' '1 debug' main
+[./example-xtrace:4] v=param1
++[./example-xtrace:5] debug
++[./example-xtrace:26] echo '[ DEBUG ]| BASH_COMMAND=[[ -z "${v}" ]]'
++[./example-xtrace:27] echo '         | BASH_ARGC=1 BASH_ARGV=param1'
++[./example-xtrace:28] echo '         | BASH_SOURCE: 0' '1 my-debug-env' ./example-xtrace
++[./example-xtrace:29] echo '         | BASH_LINENO: 0' '1 5' 0
++[./example-xtrace:30] echo '         | FUNCNAME: 0' '1 debug' main
+[./example-xtrace:5] [[ -z param1 ]]

By using this approach, while being an unlikely use case, you can also debug Bash commands from the command line using the -c option.

[me@linux ~]$ y=9
[me@linux ~]$ TRACE=1 BASH_ENV=my-debug-env bash -c "x=1; y=$y; echo \$((x+y))"
Run TRACE mode
[ DEBUG ]| BASH_COMMAND=x=1
         | BASH_ARGC=0 BASH_ARGV=
         | BASH_SOURCE: 0 my-debug-env
         | BASH_LINENO: 0 0
         | FUNCNAME: 0 debug
[ DEBUG ]| BASH_COMMAND=y=9
         | BASH_ARGC=0 BASH_ARGV=
         | BASH_SOURCE: 0 my-debug-env
         | BASH_LINENO: 0 0
         | FUNCNAME: 0 debug
[ DEBUG ]| BASH_COMMAND=echo $((x+y))
         | BASH_ARGC=0 BASH_ARGV=
         | BASH_SOURCE: 0 my-debug-env
         | BASH_LINENO: 0 0
         | FUNCNAME: 0 debug
10
[me@linux ~]$ cat xtrace.out 
+[bash:20] [[ -v NOOP ]]
+[bash:33] trap 'echo ERR trap from ${FUNCNAME:-MAIN} context. $BASH_COMMAND failed with error code $?' ERR
+[bash:34] trap debug DEBUG
++[bash:0] debug
++[bash:26] echo '[ DEBUG ]| BASH_COMMAND=x=1'
++[bash:27] echo '         | BASH_ARGC=0 BASH_ARGV='
++[bash:28] echo '         | BASH_SOURCE: 0 my-debug-env'
++[bash:29] echo '         | BASH_LINENO: 0 0'
++[bash:30] echo '         | FUNCNAME: 0 debug'
+[bash:0] x=1
++[bash:0] debug
++[bash:26] echo '[ DEBUG ]| BASH_COMMAND=y=9'
++[bash:27] echo '         | BASH_ARGC=0 BASH_ARGV='
++[bash:28] echo '         | BASH_SOURCE: 0 my-debug-env'
++[bash:29] echo '         | BASH_LINENO: 0 0'
++[bash:30] echo '         | FUNCNAME: 0 debug'
+[bash:0] y=9
++[bash:0] debug
++[bash:26] echo '[ DEBUG ]| BASH_COMMAND=echo $((x+y))'
++[bash:27] echo '         | BASH_ARGC=0 BASH_ARGV='
++[bash:28] echo '         | BASH_SOURCE: 0 my-debug-env'
++[bash:29] echo '         | BASH_LINENO: 0 0'
++[bash:30] echo '         | FUNCNAME: 0 debug'
+[bash:0] echo 10
GET UNIQUE TIPS AND THE LATEST NEWS BY SUBSCRIBING TO MY NEWSLETTER.
AND FOLLOW ME ON