Definieren Sie eine Bash-Funktion, die nicht in xtrace angezeigt wird (set -x)

Definieren Sie eine Bash-Funktion, die nicht in xtrace angezeigt wird (set -x)

In einem Bash-Skript habe ich eine log()Funktion, die an mehreren Stellen verwendet wird, sowie eine logs()Funktion, die viele Zeilen nach umleitet log(). Wenn ich Teile des Skripts mit ausführe set -x, werden natürlich auch alle Befehle innerhalb von logs()und log()verfolgt.

Ich möchte logs()und log()so definieren, dass zumindest ihr Inhalt, im besten Fall sogar ihr Aufruf bei set -xder Ausgabe unterdrückt wird.

Antwort1

Sie können nicht ändern, wie die Funktion innerhalb der Funktion aufgerufen wird. Sie können die Funktion jedoch so definieren, dass eine Untershell aufgerufen wird, die dann die Ablaufverfolgung sofort abschaltet. Beachten Sie die Klammern, die den Textkörper umgeben, anstelle der üblichen geschweiften Klammern:

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

Der Aufruf log()generiert dann nur den Aufruf selbst (aus dem vorhandenen set -xCode) und den set +xAufruf, nicht den Rest der nachfolgenden Befehle in der Funktion. Die vorhandene set -xEinstellung wird wiederhergestellt, sobald die Funktion beendet wird.

Antwort2

Ein schneller und einfacher Hack, der in allen Shells funktionieren sollte, besteht darin, Ihr Skript (vorübergehend) zu logeinem externen Skript statt einer Funktion zu machen.

In Bash können Sie auch eine Kombination aus trap '...' DEBUGund verwenden shopt -s extdebug, die vielseitiger ist als set -x. Beispiel:

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

        # 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"
}

(Natürlich können Sie auf das seltsame Format + Einrückung verzichten und es vollständig set-x-ähnlich machen; Sie können es auch in eine andere Datei umleiten, anstatt es mit dem stderr von Befehlen zu mischen.)

Dann:

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

wird darin enden, dass:

(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"
1
  (bar) shift
    (bar) case $# in
    (bar) echo "$1"
2
    (bar) shift
      (bar) case $# in
      (bar) echo "$1"
3
      (bar) shift
        (bar) case $# in
        (bar) echo "$1"
4
        (bar) shift
          (bar) case $# in

Antwort3

Um die xtrace-Ausgabe zu unterdrücken, verwenden Sie einfach set -x. Um sie wieder einzuschalten, verwenden Sie set -x. Um festzustellen, ob sie eingeschaltet ist, verwenden Sie shopt -q -o xtrace. zB

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
}

Es gibt immer noch etwas „Geplapper“, aber das ist das Beste, was ich tun kann. Um die Aufrufe von log() nicht sichtbar zu machen, können Sie jeden Aufruf von log() mit einem ähnlichen Code umgeben, anstatt ihn in die Funktion einzufügen.

verwandte Informationen