In a bash script I have a log() function that is used in several places, as well as a logs() function that diverts lots of line to log(). When I run parts of the script with set -x, obviously all commands within logs() and log() are traced too.

I would like to define logs() and log() such that at least their content, at best even their call is suppressed from set -x output.

3 Answers 3


You cannot change how the function is invoked from inside the function, but you could define the function to invoke a subshell which then immediately turns off tracing; notice the parenthesis surrounding the body instead of the typical braces:

log() (
  set +x
  # rest of log()

Invoking log() then generates only the call itself (from the existing set -x code) and the set +x call, not the rest of the subsequent commands in the function. The existing set -x setting is restored once the function exits.

  • You do need to set it back on at the end of log().
    – Chiel
    Mar 11 at 11:44
  • Are you sure? The function is defined to run in a subshell (the second set of parenthesis), so the local settings there go away with that shell.
    – Jeff Schaller
    Mar 11 at 11:51

A quick and dirty hack that should work in all shells is to (temporarily) make your log an external script instead of a function.

In bash you could also use a trap '...' DEBUG and shopt -s extdebug combination, which is much more versatile than set -x. Example:

debug() {
        local f=${FUNCNAME[1]} d=${#FUNCNAME[@]} c=$BASH_COMMAND
        if [ "$NOTRACE" ]; then
                case $debug_skip in ''|$d) debug_skip=;; *) return;; esac
                eval "case \$c in $NOTRACE) debug_skip=\$d; return; esac"

        # before the 1st command in a function XXX
        case $c in $f|"$f "*) return;; esac

        printf >&2 "%*s(%s) %s\n" $((d * 2 - 4)) "" "$f" "$c"

(Of course, you can dispense with the strange format + indenting and make it completely set-x-like; you can also redirect it to another file instead of mixed with the stderr from commands.)


NOTRACE='"log "*'
shopt -s extdebug
trap debug DEBUG

log(){ log1 "$@"; }; log1(){ log2 "$@"; }
log2(){ log3 "$@"; }; log3(){ echo "$@"; }

foo(){ foo1 "$@"; }; foo1(){ foo2 "$@"; }
foo2(){ foo3 "$@"; }; foo3(){ echo "$@"; }

bar(){ case $# in 0) ;; *) echo "$1"; shift; bar "$@";; esac; }

foo 1 2 3
log 7 8 9
bar 1 2 3 4

will result in:

(main) foo 1 2 3
  (foo) foo1 "$@"
    (foo1) foo2 "$@"
      (foo2) foo3 "$@"
        (foo3) echo "$@"
1 2 3
7 8 9
(main) bar 1 2 3 4
  (bar) case $# in
  (bar) echo "$1"
  (bar) shift
    (bar) case $# in
    (bar) echo "$1"
    (bar) shift
      (bar) case $# in
      (bar) echo "$1"
      (bar) shift
        (bar) case $# in
        (bar) echo "$1"
        (bar) shift
          (bar) case $# in

To suppress xtrace output, just use set -x. To turn it back on, use set -x. To detect if it's on, use shopt -q -o xtrace. eg

log() {
  if shopt -q -o xtrace; then TRACE_IS_ON=yes; else TRACE_IS_ON=no; fi
  set +x # turn xtrace off
  [[ "$TRACE_IS_ON" == "yes" ]] && set +x

There's still some 'chatter' but that's the best I can do. To supress seeing the calls to log() you can surround each call to log() with a similar code instead of inside the function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.