Unterdrückt den Bash-Ausführungs-Trace (set -x) von außerhalb des Skripts

17

Ich habe versucht, eine Antwort auf diese Frage zu finden, aber bisher kein Glück gehabt:

Ich habe ein Skript, das einige andere Skripte ausführt, und viele dieser anderen Skripte enthalten "set -x", wodurch sie jeden von ihnen ausgeführten Befehl drucken. Ich möchte das loswerden, aber die Informationen behalten, wenn eines der Skripte die Fehlermeldung an stderr sendet.

Also kann ich nicht einfach schreiben ./script 2>/dev/null

Außerdem habe ich keine Berechtigung zum Bearbeiten dieser anderen Skripts, sodass ich die festgelegte Option nicht manuell ändern kann.

Ich habe darüber nachgedacht, alles von stderr in die separate Datei zu protokollieren und die Tracing-Befehle herauszufiltern, aber vielleicht gibt es einen einfacheren Weg?

Mensch
quelle
1
./script 2>some_file
Satō Katsura

Antworten:

25

Mit bash4.1 und höher ist dies möglich

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(Funktioniert auch, wenn bashaufgerufen wird als sh).

Grundsätzlich empfehlen wir, bashdie xtraceAusgabe auf Dateideskriptor 7 anstatt auf Standard 2 auszugeben und diesen Dateideskriptor auf umzuleiten /dev/null. Die fd Nummer ist beliebig. Verwenden Sie ein FD über 2, das in Ihrem Skript nicht anders verwendet wird. Wenn die Shell, in der Sie diesen Befehl eingeben, bashoder ist yash, können Sie sogar eine Zahl über 9 verwenden (obwohl Probleme auftreten können, wenn der Dateideskriptor intern von der Shell verwendet wird).

Wenn die Shell, von der aus Sie das bashSkript aufrufen, ist zsh, können Sie auch Folgendes tun:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

für die Variable wird automatisch das erste freie fd über 9 vergeben.

Für ältere Versionen bash, eine andere Option, wenn die xtracemit eingeschaltet wird set -x(im Gegensatz zu #! /bin/bash -xoder set -o xtrace) wäre neu zu definieren setals eine exportierte Funktion , die nichts tut , wenn übergeben -x(obwohl das das Skript brechen würde , wenn er (oder irgendein anderes bashSkript sie aufruft) verwendet , um setdie Positionsparameter einzustellen).

Mögen:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

Eine andere Option ist das Hinzufügen eines DEBUG-Traps in einer $BASH_ENVDatei, die set +xvor jedem Befehl ausgeführt wird.

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

Das geht aber nicht, wenn set -xin einer Sub-Shell gearbeitet wird.

Wie @ilkkachu sagte, sollten Sie, vorausgesetzt, Sie haben Schreibberechtigung für einen Ordner im Dateisystem, zumindest in der Lage sein, eine Kopie des Skripts zu erstellen und es zu bearbeiten.

Wenn Sie nirgendwo eine Kopie des Skripts schreiben können oder wenn es nicht bequem ist, bei jeder Aktualisierung des Originalskripts eine neue Kopie zu erstellen und zu bearbeiten, können Sie möglicherweise folgende Aktionen ausführen:

 bash <(sed 's/set -x/set +x/g' ./script.bash)

Dies (und der Kopieransatz) funktioniert möglicherweise nicht richtig, wenn das Skript etwas $0Besonderes mit oder spezielle Variablen ausführt $BASH_SOURCE(z. B. nach Dateien suchen, die sich auf den Speicherort des Skripts selbst beziehen), sodass Sie möglicherweise noch etwas nachbearbeiten müssen Ersetzen Sie $0mit dem Pfad des Skripts ...

Stéphane Chazelas
quelle
Ihre erste Antwort ist genau das, was ich brauchte, sauber und elegant. Könntest du ein wenig erklären, wie es funktioniert? Warum die Nummer 7? Wofür könnten andere Nummern verwendet werden? Vielen Dank.
Mensch
@human, siehe Bearbeiten
Stéphane Chazelas
1
Der {BASH_XTRACEFD}>Trick funktioniert auch in bash4.1 oder höher.
Chepner
@chepner, ja, die Funktion wurde auf Vorschlag eines zsh-Entwicklers gleichzeitig zu zsh, ksh93 und bash hinzugefügt. Aber hier funktioniert es nicht für ksh93oder bashdadurch , dass der Variable in der Umgebung des Befehls nicht bestanden (vergleiche <shell> -c 'export fd; printenv fd {fd}> /dev/null'in zsh, bashund ksh93). Man kann es in machen arbeiten ksh93/ bashindem sie sie in zwei Schritten tun oder möglicherweise unter Verwendung eval, sondern auch für bash, die Nebenwirkungen , wenn die xtrace Option war hätte.
Stéphane Chazelas
5

Da sie Skripte sind, Sie könnten Kopien von ihnen machen, und bearbeiten diese.

Abgesehen davon, dass das Filtern der Ausgabe einfach erscheint, können Sie explizit PS4etwas Ungewöhnlicheres als ein einzelnes Plus festlegen , um das Filtern zu vereinfachen:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(Natürlich wird das stdout und stdin kollabieren, aber das Leiten von nur stderr in Bash wird ein bisschen haarig, also werde ich das ignorieren.)

ilkkachu
quelle
1
Nur Stderr kochend ist einfach: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Patrick
4
@Patrick, dies zu tun, bashhat Probleme, da grepes asynchron ausgeführt wird (bash wartet nicht darauf, sodass es Dinge ausgeben kann (und dies auch oft tut), nachdem der nächste Befehl im Skript gestartet wurde).
Stéphane Chazelas