Is it safe to eval $BASH_COMMAND?

One possibility is to make a wrapper function that will at the same time print the command and execute it, as follows:

debug() {
    # This function prints (to stdout) its arguments and executes them
    local args=() idx=0 IFS=' ' c
    for c; do printf -v args[idx++] '%q' "$c"; done
    printf "%s\n" "${args[*]}"
    # Execute!
    "$@"
}

So that in your script you can do:

debug echo "$ARG"

No need to fiddle with the trap. The drawback is that it adds some debug keywords all over your code (but that should be fine, it's common to have such stuff, like asserts, etc.).

You can even add a global variable DEBUG and modify the debug function like so:

debug() {
    # This function prints (to stdout) its arguments if DEBUG is non-null
    # and executes them
    if [[ $DEBUG ]]; then
        local args=() idx=0 IFS=' ' c
        for c; do printf -v args[idx++] '%q' "$c"; done
        printf "%s\n" "${args[*]}"
    fi
    # Execute!
    "$@"
}

Then you can call your script as:

$ DEBUG=yes ./myscript

or

$ DEBUG= ./myscript

or just

$ ./myscript

depending whether you want to have the debug info or not.

I capitalized the DEBUG variable because it should be treated as an environment variable. DEBUG is a trivial and common name, so this might clash with other commands. Maybe call it GNIOURF_DEBUG or MARTIN_VON_WITTICH_DEBUG or UNICORN_DEBUG if you like unicorns (and then you probably like ponies too).

Note. In the debug function, I carefully formatted each argument with printf '%q' so that the output will be correctly escaped and quoted so as to be reusable verbatim with a direct copy and paste. It will also show you exactly what the shell saw as you'll be able to figure out each argument (in case of spaces or other funny symbols). This function also uses direct assignment with the -v switch of printf so as to avoid unnecessary subshells.


eval "$BASH_COMMAND" runs the command.

printf '%s\n' "$BASH_COMMAND" prints the exact specified command, plus a newline.

If the command contains variables (i.e. if it's something like cat "$foo"), then printing out the command prints out the variable text. It is impossible to print the variable's value without executing the command — think of commands like variable=$(some_function) other_variable=$variable.

The simplest way to get a trace from executing a shell script is to set the xtrace shell option by running the script as bash -x /path/to/script or invoking set -x inside the shell. The trace is printed to standard error.

Tags:

Bash