How can I debug a Bash script? [closed]
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Is there a way to debug a Bash script? E.g., something that prints a sort of an execution log, like «calling line 1», «calling line 2», etc.
12 Answers 12
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:
This turns debugging on. You can then turn it off again with:
(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 arithmetic for commands and their arguments or associated word lists after they are expanded and before they are executed. The value of the PS4 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.
How to debug a bash script?
I’m having some problems with some scripts in bash, about errors and unexpected behaviors. I would like to investigate the causes of the problems so I can apply fixes. Is there a way I can turn some kind of «debug-mode» for bash, to get more information?
8 Answers 8
Start your bash script with bash -x ./script.sh or add in your script set -x to see debug output.
Additional with bash 4.1 or later:
If you want to write the debug output to a separate file, add this to your script:
exec 5> debug_output.txt BASH_XTRACEFD="5"
If you want to see line numbers add this:
If you have access to logger command then you can use this to write debug output via your syslog with timestamp, script name and line number:
#!/bin/bash exec 5> >(logger -t $0) BASH_XTRACEFD="5" PS4='$LINENO: ' set -x # Place your code here
You can use option -p of logger command to set an individual facility and level to write output via local syslog to its own logfile.
-v can also help (prints out each line as they get executed. can be combined with -x). And see also : bashdb.sourceforge.net
@aggsol: If you use BASH_XTRACEFD=»5″ bash writes the trace output generated when set -x is enabled to file descriptor 5. exec 5> >(logger -t $0) redirects output from file descriptor 5 to logger command.
Using set -x
I always use set -x and set +x . You can wrap areas that you want to see what’s happening with them to turn verbosity up/down.
#!/bin/bash set -x ..code to debug. set +x
log4bash
Also if you’ve done development work and are familiar with the style of loggers that go by the names log4j, log4perl, etc., then you might want to make use of log4bash.
Let’s face it — plain old echo just doesn’t cut it. log4bash is an attempt to have better logging for Bash scripts (i.e. make logging in Bash suck less).
From there you can do things like this in your Bash scripts:
#!/usr/bin/env bash source log4bash.sh log "This is regular log message. log and log_info do the same thing"; log_warning "Luke . you turned off your targeting computer"; log_info "I have you now!"; log_success "You're all clear kid, now let's blow this thing and go home."; log_error "One thing's for sure, we're all gonna be a lot thinner."; # If you have figlet installed -- you'll see some big letters on the screen! log_captains "What was in the captain's toilet?"; # If you have the "say" command (e.g. on a Mac) log_speak "Resistance is futile";
Resulting in this type of output:
log4sh
If you need something more portable there’s also the older log4sh . Works similar to log4bash , available here: