Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Bash is able to trace execution script commands with the -x command line option. Each command is then output to stderr, prefixed by PS4 as stated in the man page.

 -x      After expanding each simple command, for command,  case  command,
         select  command,  or arithmetic for command, display the expanded
         value of PS4, followed by the command and its expanded  arguments
         or associated word list.

Albeit useful, the resolved executable path of each command is not given. So I have to figure out which executable has been run by guessing shell aliases and PATH value while reading execution trace.

For example, ruby has multiple command candidates on my workstation:

$ type -a ruby
ruby is /home/cbliard/.rvm/rubies/ruby-2.1.0/bin/ruby
ruby is /home/cbliard/.rvm/bin/ruby
ruby is /usr/bin/ruby
ruby is /home/cbliard/.rvm/bin/ruby

If executing this script with /bin/bash -x

#!/bin/bash
echo "compute"
ruby script/run_something.rb
echo "deploy"
bundle install
bundle exec cap deploy

Trace execution will be

+ echo "compute"
compute
+ ruby script/run_something.rb
[output of ruby script]
+ bundle install
Using rake (10.1.0)
Using i18n (0.6.9)
[...]
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
+ bundle exec cap deploy
    triggering load callbacks
  * 2014-01-14 09:12:42 executing `deploy'
  * 2014-01-14 09:12:42 executing `deploy:update'
[...]
Finished: SUCCESS

I had some oddities with resolved executables, mainly due to my usage of rvm. The output I would like is with fully resolved path, like this:

+ [builtin] echo "compute"
compute
+ /home/cbliard/.rvm/rubies/ruby-1.9.3-p484/bin/ruby script/run_something.rb
[output of ruby script]
+ /home/cbliard/.rvm/gems/ruby-1.9.3-p484@global/bin/bundle install
Using rake (10.1.0)
Using i18n (0.6.9)
[...]
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
+ /home/cbliard/.rvm/gems/ruby-1.9.3-p484@global/bin/bundle exec cap deploy
    triggering load callbacks
  * 2014-01-14 09:12:42 executing `deploy'
  * 2014-01-14 09:12:42 executing `deploy:update'
[...]
Finished: SUCCESS

Having the resolved command would really help me debugging weird rvm/bundler issues. The -x option helps a lot but sometimes it is not enough. Is there any trick that helps knowing the full characteristics of the commands while running a bash script?. It is better if the original script does not have to be modified.

share|improve this question
    
You could give your script such a command line option. –  goldilocks Jan 13 at 14:07
    
Yes, but doing it without modifying the script would be better. I saw on binfalse.de/2012/09/howto-debug-bash-scripts that PS4 can be customized. Maybe it is possible to customize this prompt more and make it display the full executable path. –  cbliard Jan 13 at 14:25
    
@rhamu and other closers, before voting to close, please add a comment to say what you find unclear in the question. I personally find it as clear as can be, so wouldn't know how to improve it (and I was not the one asking the question). –  Stéphane Chazelas Jan 14 at 8:00
    
I am currently editing my question to make it clearer –  cbliard Jan 14 at 8:02
    
@rhamu & others Is it better like this? How can I improve? –  cbliard Jan 14 at 8:25

1 Answer 1

up vote 4 down vote accepted

As an approximation, you could do:

trap '{ type -p -- "${BASH_COMMAND%% *}" >&3; } 3>&2 2> /dev/null' DEBUG
set -o functrace -o xtrace

The DEBUG trap is run before every command. During the execution of that trap, $BASH_COMMAND is set to the current command. That includes function calls, builtins, assignments... We call type -p on the first part of that up to the first space character with type's stdout retdirected to the shell's stderr (via fd 3, so that the xtrace output is redirected to /dev/null).

That's an approximation in that it won't work in cases like "cmd" foo or $CMD foo. Like for xtrace, beware of redirections of stderr.

Without modifying the script:

BASH_ENV=<(cat <<'EOF'
  trap '{ type -p -- "${BASH_COMMAND%% *}" >&3; } 3>&2 2> /dev/null' DEBUG
EOF) bash -o functrace -x your-script

Or to have it in the PS4 prompt:

BASH_ENV=<(cat <<'EOF'
  { trap '{ cmdpath=$(type -p -- "${BASH_COMMAND%% *}")
      } 2> /dev/null' DEBUG;} 2> /dev/null
EOF) PS4='+[$cmdpath] ' bash -o functrace -x your-script

or to avoid the fork:

BASH_ENV=<(cat <<'EOF'
  { trap '{ hash -- "${BASH_COMMAND%% *}";} 2> /dev/null' DEBUG;}2>/dev/null
EOF) PS4='+[${BASH_CMDS[${BASH_COMMAND%% *}]}] ' bash -o functrace -x your-script
share|improve this answer
    
Useful hack. Thanks! I used type without the -p option. –  cbliard Jan 13 at 16:29

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.