Bash set +x without it being printed

BashShell

Bash Problem Overview


Does anyone know if we can say set +x in bash without it being printed:

set -x
command
set +x

traces

+ command
+ set +x

but it should just print

+ command

Bash is Version 4.1.10(4). This is bugging me for some time now - output is cluttered with useless set +x lines, making the trace facility not as useful as it could be.

Bash Solutions


Solution 1 - Bash

I had the same problem, and I was able to find a solution that doesn't use a subshell:

set -x
command
{ set +x; } 2>/dev/null

Solution 2 - Bash

You can use a subshell. Upon exiting the subshell, the setting to x will be lost:

( set -x ; command )

Solution 3 - Bash

How about a solution based on a simplified version of @user108471:

shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ set +x; } 2>/dev/null'

trace_on
...stuff...
trace_off

Solution 4 - Bash

I hacked up a solution to this just recently when I became annoyed with it:

shopt -s expand_aliases
_xtrace() {
	case $1 in
		on) set -x ;;
		off) set +x ;;
	esac
}
alias xtrace='{ _xtrace $(cat); } 2>/dev/null <<<'

This allows you to enable and disable xtrace as in the following, where I'm logging how the arguments are assigned to variables:

xtrace on
ARG1=$1
ARG2=$2
xtrace off

And you get output that looks like:

$ ./script.sh one two
+ ARG1=one
+ ARG2=two

Solution 5 - Bash

This is a combination of a few ideas that can enclose a block of code and preserves the exit status.

#!/bin/bash
shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ PREV_STATUS=$? ; set +x; } 2>/dev/null; (exit $PREV_STATUS)'

trace_on
echo hello
trace_off
echo "status: $?"

trace_on
(exit 56)
trace_off
echo "status: $?"

When executed:

$ ./test.sh 
+ echo hello
hello
status: 0
+ exit 56
status: 56

Solution 6 - Bash

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndreas SpindlerView Question on Stackoverflow
Solution 1 - BashMcJoeyView Answer on Stackoverflow
Solution 2 - BashchorobaView Answer on Stackoverflow
Solution 3 - BashOliverView Answer on Stackoverflow
Solution 4 - Bashuser108471View Answer on Stackoverflow
Solution 5 - Bashuser3286792View Answer on Stackoverflow
Solution 6 - BashAndreas SpindlerView Answer on Stackoverflow