I'm writing a shell script that loops over some values and run a long command line for each value. I'd like to print out these commands along the way, just like make
does when running a makefile. I know I could just "echo" all commands before running them, but it feels inelegant. So I'm looking at set -x
and similar mechanisms instead :
#!/bin/sh
for value in a long list of values
do
set -v
touch $value # imagine a complicated invocation here
set +v
done
My problem is: at each iteration, not only is the interresting line printed out, but also the set +x
line as well. Is it somehow possible to prevent that ? If not, what workaround do you recommend ?
PS: the MWE above uses sh
, but I also have bash
and zsh
installed in case that helps.
To silence the output of a command, we redirect either stdout or stderr — or both — to /dev/null. To select which stream to redirect, we need to provide the FD number to the redirection operator.
Use the break statement to exit a while loop when a particular condition realizes. The following script uses a break inside a while loop: #!/bin/bash i=0 while [[ $i -lt 11 ]] do if [[ "$i" == '2' ]] then echo "Number $i!" break fi echo $i ((i++)) done echo "Done!"
Basic Syntax of script Command To stop script, type exit and press [Enter]. If the script can not write to the named log file then it shows an error.
Only when the last program closes its handle does the file really get deleted. So, in a sense, yes, a shell script can delete itself, but it won't really be deleted until after the execution of that script finishes.
Sandbox it in a subshell:
(set -x; do_thing_you_want_traced)
Of course, changes to variables or the environment made in that subshell will be lost.
If you REALLY care about this, you could also use a DEBUG trap (using set -T
to cause it to be inherited by functions) to implement your own set -x
equivalent.
For instance, if using bash:
trap_fn() {
[[ $DEBUG && $BASH_COMMAND != "unset DEBUG" ]] && \
printf "[%s:%s] %s\n" "$BASH_SOURCE" "$LINENO" "$BASH_COMMAND"
return 0 # do not block execution in extdebug mode
}
trap trap_fn DEBUG
DEBUG=1
# ...do something you want traced...
unset DEBUG
That said, emitting BASH_COMMAND (as a DEBUG trap can do) is not fully equivalent of set -x
; for instance, it does not show post-expansion values.
You want to try using a single-line xtrace:
function xtrace() {
# Print the line as if xtrace was turned on, using perl to filter out
# the extra colon character and the following "set +x" line.
(
set -x
# Colon is a no-op in bash, so nothing will execute.
: "$@"
set +x
) 2>&1 | perl -ne 's/^[+] :/+/ and print' 1>&2
# Execute the original line unmolested
"$@"
}
The original command executes in the same shell under an identity transformation. Just prior to running, you get a non-recursive xtrace of the arguments. This allows you to xtrace the commands you care about without spamming stederr with duplicate copies of every "echo" command.
# Example
for value in $long_list; do
computed_value=$(echo "$value" | sed 's/.../...')
xtrace some_command -x -y -z $value $computed_value ...
done
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With