How to properly debug a bash script
The best way is to use set -xv
. The set -v
will echo a line before it is executed. The set -x
will output the line after the shell script interpolates the variables and expressions in that line.
As part of this, you can also create an environment variable called PS4
which will be the prompt printed each time your shell scripts outputs the line being executed. Most people set it to something like PS="\$LINENO: "
which will print out the line number for the line being executed.
Once you're finished, you can turn off debugging by setting set +xv
.
#
# Somewhere in this part of my script, I am having problems....
#
export PS4="\$LINENO> " # Now, I'll see the line numbers while debugging
set -xv # Debugging is turned on
....
#
# Done with debugging
#
set +xv # Debugging is turned off
How execute bash script line by line?
You don't need to put a read in everyline, just add a trap like the following into your bash script, it has the effect you want, eg.
#!/usr/bin/env bash
set -x
trap read debug
< YOUR CODE HERE >
Works, just tested it with bash v4.2.8 and v3.2.25.
IMPROVED VERSION
If your script is reading content from files, the above listed will not work. A workaround could look like the following example.
#!/usr/bin/env bash
echo "Press CTRL+C to proceed."
trap "pkill -f 'sleep 1h'" INT
trap "set +x ; sleep 1h ; set -x" DEBUG
< YOUR CODE HERE >
To stop the script you would have to kill it from another shell in this case.
ALTERNATIVE1
If you simply want to wait a few seconds before proceeding to the next command in your script the following example could work for you.
#!/usr/bin/env bash
trap "set +x; sleep 5; set -x" DEBUG
< YOUR CODE HERE >
I'm adding set +x and set -x within the trap command to make the output more readable.
How can I debug a Bash function that returns a value, and how can I add newlines to a variable?
Why the newlines seemed to disappear from the variable
The newlines are actually retained in the variable. They do not display because the variable in the echo
statement is not enclosed in double-quotes. From the code:
echo $x
When using a variable without double-quotes, word splitting is performed. Under the default $IFS
(wiki entry on IFS), this means that all collections of whitespace, including newlines and tabs, are replaced with single space.
To avoid that, simply use double quotes as in:
echo "$x"
With that single change, the output of your script becomes:
$ bash a,sh
---Pre function
hello
world
Hello
worldhello
world
hello
world
---In function
hello
world
Hello
worldhello
world
hello
world
---Post function
hello
world
Hello
worldhello
world
hello
world
The newlines that were always in the variable x
are now displayed.
Aside: the two words that remain strung together
Note that the combination worldhello
appears on one line because because that is what the code asked for:
printf "Hello\nworld"
echo $'hello\nworld'
The printf
does not print a newline after world
. Hence, that world
appears on the same line as the hello
which follows.
Documentation of the Details
man bash
explains that double-quotes inhibit word splitting:
If the substitution appears within double quotes, word splitting and pathname expansion are not performed on the results.
Word-splitting happens after variable expansion, command
substitution, and arithmetic expansion:
The shell scans the results of parameter expansion, command
substitution, and arithmetic expansion that did not occur within
double quotes for word splitting.
Another subtlety is that word splitting is performed only if some substitution took place:
Note that if no expansion occurs, no splitting is performed.
Normally, when word splitting is performed, all strings of spaces, tabs, and newlines are replaced by a single space. This default behavior can be changed by changing the value of the IFS
variable:
The shell treats each character of IFS as a delimiter, and splits the
results of the other expansions into words on these characters. If
IFS is unset, or its value is exactly , the
default, then sequences of space, tab, and newline at the
beginning and end of the results of the previous expansions are
ignored, and any sequence of IFS characters not at the beginning or
end serves to delimit words. If IFS has a value other than the
default, then sequences of the whitespace characters space and tab are
ignored at the beginning and end of the word, as long as
the whitespace character is in the value of IFS (an IFS whitespace
character). Any character in IFS that is not IFS whitespace, along
with any adjacent IFS whitespace characters, delimits a field. A
sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.
How to Debug
Use
set -x
Place the line
set -x
at the beginning of the code that you wish to run. The results of evaluating each line will be displayed as the function is run, each preceded byPS4
(default is+
, space) to distinguish it from normal output.The debug output can be turned off by including the line
set +x
.set -x
andset +x
both also work on the command line.Use
stderr
Send debug output to
stderr
(file descriptor 2) as follows:echo "My Debug Info" >&2
By default, pipelines and command substitutions only operate on
stderr
. Consequently, information sent tostderr
will, by default, appear on the terminal.
More on echo
By default, echo
ignores escape characters and the sequence \n
simply means a \
followed by an n
:
$ echo "Hello\nworld 4"
Hello\nworld 4
To have \n
interpreted as a newline, use -e
:
$ echo -e "Hello\nworld 4"
Hello
world 4
How to debug a shell script invoked with exec?
You can also export SHELLOPTS
from foo.bash
as well to export the shell options.
export SHELLOPTS
How can I debug the bash prompt?
Most of the shells have debug flags that show the commands being executed. Bash may even have one that shows a command before expansion of variables and after. Have you tried checking (I believe) -c -x or -X flags and see if they show the information you are looking for.
You can set them as first thing in the rc files (most global one) or just pass it down into bash command by invoking it from another shell.
In fact, if you invoke bash from another shell, you can also use script command to record everything you see and do into the file, which makes postmortem analysis so much easier.
bash: debug option and functions
With bash, you can use functrace
option in your script
set -o functrace
See manpage for bash
for other debugger options.
Graphical debugger for bash
for debugging execute your script with:
bash -x <scriptname>
gui debugger:
http://bashdb.sourceforge.net/
Is there a GUI debugger for shell scripts
A useful trick is to start your (executable) bash script with
#!/bin/bash -vx
at least during the debugging phase; you'll get a lot of helpful messages from bash. See bash(1)
If your script is not supposed to be used in a terminal (e.g. it is invoked from a crontab(5) entry) you could also use the logger(1) command.
At last, I believe that bash
is not the right tool to do complex scripting. If your script is becoming complex or bigger than a few dozens of lines, consider switching to a better scripting language (like GNU guile, python, gawk, etc...). In general, you should not code a lot in bash
. Having pain to debug a bash script is usually a symptom that you should recode that script in a better language.
You may also find useful to generate your shell script. autogen could be useful for that.
Related Topics
Cdc_Acm: Failed to Set Dtr/Rts - Can Not Communicate with Usb Cdc Device
How to Execute The Vim Commands Through Shell Script
Using Git to Clone from a Windows Machine to a Linux Webserver (In House)
Linux - Create Animated Gif with Pan and Zoom
How to Write Kernel Space Memory (Physical Address) to a File Using O_Direct
How to Run a Cron Job with Arguments and Pass Results to a Log
Command-Line Fulltext Indexing
Create Infinite Looping Repeating File Cat in Linux/Bash
Output Data Register Value in Nasm
How _Libc_Start_Main@Plt Works
Change Conda Default Pkgs_Dirs and Envs Dirs
How to Split Two Vertical Pane Inside a Horizontal Pane in Tmux Using Tmuxinator
Change a String in a File with Sed
How to Redirect Entire Output of Spark-Submit to a File
Replace Forward Slash with Double Backslash Enclosed in Double Quotes
Git Error: Gpg Failed to Sign The Data on Linux
What Is The Current State of Tail-Call-Optimization for F# on Mono (2.11)