Is there any way to debug a bash script? E.g something that prints a sort of execution log like "calling line 1", "calling line 2" etc.
问题:
回答1:
sh -x script [arg1 ...] bash -x script [arg1 ...]
These give you a trace of what is being executed. (See also 'Clarification' near the bottom of the answer.)
Sometimes, you need to control the debugging within the script. In that case, as Cheeto reminded me, you can use:
set -x
This turns debugging on. You can then turn it off again with:
set +x
(You can find out the current tracing state by analyzing $-
, the current flags, for x
.)
Also, shells generally provide options '-n
' for 'no execution' and '-v
' for 'verbose' mode; you can use these in combination to see whether the shell thinks it could execute your script ― occasionally useful if you have an unbalanced quote somewhere.
There is contention that the '-x
' option in Bash is different from other shells (see the comments). The Bash Manual says:
-x
Print a trace of simple commands,
for
commands,case
commands,select
commands, and arithmeticfor
commands and their arguments or associated word lists after they are expanded and before they are executed. The value of thePS4
variable is expanded and the resultant value is printed before the command and its expanded arguments.
That much does not seem to indicate different behaviour at all. I don't see any other relevant references to '-x
' in the manual. It does not describe differences in the startup sequence.
Clarification: On systems such as a typical Linux box, where '/bin/sh
' is a symlink to '/bin/bash
' (or wherever the Bash executable is found), the two command lines achieve the equivalent effect of running the script with execution trace on. On other systems (for example, Solaris, and some more modern variants of Linux), /bin/sh
is not Bash, and the two command lines would give (slightly) different results. Most notably, '/bin/sh
' would be confused by constructs in Bash that it does not recognize at all. (On Solaris, /bin/sh
is a Bourne shell; on modern Linux, it is sometimes Dash ― a smaller, more strictly POSIX-only shell.) When invoked by name like this, the 'shebang' line ('#!/bin/bash
' vs '#!/bin/sh
') at the start of the file has no effect on how the contents are interpreted.
The Bash manual has a section on Bash POSIX mode which, contrary to a long-standing but erroneous version of this answer (see also the comments below), does describe in extensive detail the difference between 'Bash invoked as sh
' and 'Bash invoked as bash
'.
When debugging a (Bash) shell script, it will be sensible and sane ― necessary even ― to use the shell named in the shebang line with the -x
option. Otherwise, you may (will?) get different behaviour when debugging from when running the script.
回答2:
I've used the following methods to debug my script.
set -e
makes the script stop immediately if any external program returns a non-zero exit status. This is useful if your script attempts to handle all error cases and where a failure to do so should be trapped.
set -x
was mentioned above and is certainly the most useful of all the debugging methods.
set -n
might also be useful if you want to check your script for syntax errors.
strace
is also useful to see what's going on. Especially useful if you haven't written the script yourself.
回答3:
This answer is valid and useful: https://stackoverflow.com/a/951352
But, I find that the "standard" script debugging methods are inefficient, unintuitive, and hard to use. For those used to sophisticated GUI debuggers that put everything at your fingertips and make the job a breeze for easy problems (and possible for hard problems), these solutions aren't very satisfactory.
What I do is use a combination of DDD and bashdb. The former executes the latter, and the latter executes your script. This provides a multi-window UI with the ability to step through code in context and view variables, stack, etc., without the constant mental effort to maintain context in your head or keep re-listing the source.
There is guidance on setting that up here: http://ubuntuforums.org/showthread.php?t=660223
回答4:
You can also write "set -x" within the script.
回答5:
I found shellcheck utility and may be some folks find it interesting https://github.com/koalaman/shellcheck
A little example:
$ cat test.sh ARRAY=("hello there" world) for x in $ARRAY; do echo $x done $ shellcheck test.sh In test.sh line 3: for x in $ARRAY; do ^-- SC2128: Expanding an array without an index only gives the first element.
fix the bug, first try...
$ cat test.sh ARRAY=("hello there" world) for x in ${ARRAY[@]}; do echo $x done $ shellcheck test.sh In test.sh line 3: for x in ${ARRAY[@]}; do ^-- SC2068: Double quote array expansions, otherwise they're like $* and break on spaces.
Let's try again...
$ cat test.sh ARRAY=("hello there" world) for x in "${ARRAY[@]}"; do echo $x done $ shellcheck test.sh
find now!
It's just a small example.
回答6:
I built a Bash debugger. Just give it a try. I hope it will help https://sourceforge.net/projects/bashdebugingbash
回答7:
I think you can try this Bash debugger: http://bashdb.sourceforge.net/.
回答8:
Install VSCode, then add bash debug extension and you are ready to debug in visual mode. see Here in action.
回答9:
set +x = @ECHO OFF, set -x = @ECHO ON.
You can add -xv
option to the standard Shebang as follows:
#!/bin/bash -xv
-x
: Display commands and their arguments as they are executed.-v
: Display shell input lines as they are read.
ltrace
is another Linux Utility similar to strace
. However, ltrace
lists all the library calls being called in an executable or a running process. Its name itself comes from library-call tracing. For example:
ltrace ./executable ltrace -p
回答10:
Some trick to debug bash scripts:
Using set -[nvx]
In addition to
set -x
and
set +x
for stopping dump.
I would like to speak about set -v
wich dump as smaller as less developped output.
bash &1 >/dev/null|wc -l 21 for arg in x v n nx nv nvx;do echo "- opts: $arg" bash 2> >(wc -l|sed s/^/stderr:/) > >(wc -l|sed s/^/stdout:/)
Dump variables or tracing on the fly
For testing some variables, I use sometime this:
bash &2 -p var1 var2' myscript.sh) args
for adding:
declare >&2 -p var1 var2
at line 18 and running resulting script (with args), without having to edit them.
of course, this could be used for adding set [+-][nvx]
:
bash \&2/;22s/^/set -x\n/;26s/^/set +x\n/' myscript) args
will add declare -p v1 v2 >&2
after line 18, set -x
before line 22 and set +x
before line 26.
little sample:
bash \&2/;5s/^/set -x\n/;7s/^/set +x\n/'
Note: Care about $LINENO
will be affected by on-the-fly modifications!
( To see resulting script whithout executing, simply drop bash and
) arg1 arg2
)
Step by step, execution time
Have a look at my answer about how to profile bash scripts
回答11:
Use eclipse with the plugins shelled & basheclipse.
https://sourceforge.net/projects/shelled/?source=directory https://sourceforge.net/projects/basheclipse/?source=directory
For shelled: Download the zip and import it into eclipse via help -> install new software : local archive For basheclipse: Copy the jars into dropins directory of eclipse
Follow the steps provides https://sourceforge.net/projects/basheclipse/files/?source=navbar
I wrote a tutorial with many screenshots at http://dietrichschroff.blogspot.de/2017/07/bash-enabling-eclipse-for-bash.html
回答12:
There's good amount of detail on logging for shell scripts via global varaibles of shell. We can emulate the similar kind of logging in shell script: http://www.cubicrace.com/2016/03/log-tracing-mechnism-for-shell-scripts.html
The post has details on introdducing log levels like INFO , DEBUG, ERROR. Tracing details like script entry, script exit, function entry, function exit.
Sample log: