Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to silently disable xtrace in a shell script?

Tags:

shell

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.

like image 806
Gyom Avatar asked Jun 28 '13 13:06

Gyom


People also ask

How do you silent a bash script?

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.

How do I stop a shell script from looping?

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!"

How do I stop a script command?

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.

Can a bash script remove itself?

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.


2 Answers

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.

like image 171
Charles Duffy Avatar answered Sep 20 '22 00:09

Charles Duffy


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
like image 37
Dave Dopson Avatar answered Sep 23 '22 00:09

Dave Dopson