Here is another one. Often you only want to trace a particular command in your script. Then why not write a function that does just that?
> call() { set -x; "$@"; { set +x; } 2>/dev/null; } > call uname -a + uname -a CYGWIN_NT-6.1-WOW W530 3.1.7(0.340/5/3) 2020-08-22 19:03 i686 Cygwin > call make -j8 *.mak + make -j8 some_name.mak
We can improve this further by returning (and tracing) the exit code of the called command:
> call() { local rc; set -x; "$@"; rc=$?; { set +x; } 2>/dev/null; return $rc; } > call true && echo yes + true + rc=0 yes > call false && echo yes + false + rc=1
Here is a real life example that calls the Rhapsody CLI program to generate source code from a .rcl-file with command-line options:
die() { local c=${1:--1} m=${2:-'Died'} echo "$m at ${BASH_SOURCE[1]}:${FUNCNAME[1]} line ${BASH_LINENO[0]} (exit $c)" >&2 exit $c } call() { local rc; set -x; "$@"; rc=$?; { set +x; } 2>/dev/null; return $rc; } call "$opt_rhapsodycl" -f $rclfile || die $? 'Rhapsody license server not reachable'
For example, in case of failure prints something like:
+ path/to/RhapsodyCL.exe -f configuration.rcl + rc=127 Rhapsody license server not reachable at ./Build:main line 167 (exit 127)
Or in case of success the script continues. With these two functions (call and die) we can write very compact and readable one-liners that also produce nice tracing output.
script.sh 2>&1 | grep -v 'set +x'