Ausführungsverfolgung für Echo-Befehl unterdrücken?

Ausführungsverfolgung für Echo-Befehl unterdrücken?

Ich führe Shell-Skripte von Jenkins aus, das Shell-Skripte mit den Shebang-Optionen startet #!/bin/sh -ex.

EntsprechendBash Shebang für Dummies?, -x, „bewirkt, dass die Shell einen Ausführungsverlauf druckt“, was für die meisten Zwecke großartig ist – außer für Echos:

echo "Message"

erzeugt die Ausgabe

+ echo "Message"
Message

was ein bisschen redundant ist und ein bisschen seltsam aussieht. Gibt es eine Möglichkeit, -xaktiviert zu lassen, aber nur die Ausgabe

Message

anstelle der beiden Zeilen oben, z. B. indem Sie dem Echo-Befehl ein spezielles Befehlszeichen voranstellen oder die Ausgabe umleiten?

Antwort1

Wenn man bis zum Hals in Alligatoren steckt, vergisst man leicht, dass das Ziel darin bestand, den Sumpf trockenzulegen.                    — beliebtes Sprichwort

Die Frage istecho, und dennoch konzentrierte sich die Mehrheit der bisherigen Antworten darauf, wie man einen set +xBefehl einschmuggelt. Es gibt eine viel einfachere, direktere Lösung:

{ echo "Message"; } 2> /dev/null

(Ich gebe zu, dass ich möglicherweise nicht daran gedacht hätte, { …; } 2> /dev/null wenn ich es nicht in den früheren Antworten gesehen hätte.)

Dies ist etwas umständlich, aber wenn Sie einen Block aufeinanderfolgender echoBefehle haben, müssen Sie dies nicht für jeden Befehl einzeln tun:

{
  echo "The quick brown fox"
  echo "jumps over the lazy dog."
} 2> /dev/null

Beachten Sie, dass Sie bei Zeilenumbrüchen keine Semikolons benötigen.

Sie können den Tippaufwand verringern, indem Siekenorbs Idee/dev/nulldauerhaft mit einem nicht standardmäßigen Dateideskriptor (z. B. 3) zu öffnen und dann ständig „ 2>&3stattdessen“ zu sagen.2> /dev/null


Die ersten vier Antworten zum Zeitpunkt des Schreibens dieses Artikels erfordern etwas Besonderes (und in den meisten Fällen Umständliches) jedenechoWenn du wirklich willst ,alle echoBefehle zum Unterdrücken der Ausführungsverfolgung (und warum sollten Sie das nicht tun?), können Sie dies global tun, ohne viel Code zu verändern. Zuerst fiel mir auf, dass Aliase nicht verfolgt werden:

$ myfunc()
> {
>     date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016  0:00:00 AM           # Happy Halloween!
$ myfunc
+ myfunc                                # Note that function call is traced.
+ date
Mon, Oct 31, 2016  0:00:01 AM
$ myalias
+ date                                  # Note that it doesn’t say  + myalias
Mon, Oct 31, 2016  0:00:02 AM

(Beachten Sie, dass die folgenden Skriptausschnitte funktionieren, wenn der Shebang lautet #!/bin/sh, auch wenn es /bin/shsich um einen Link zu Bash handelt. Wenn der Shebang jedoch lautet #!/bin/bash, müssen Sie einen shopt -s expand_aliasesBefehl hinzufügen, damit Aliase in einem Skript funktionieren.)

Also, mein erster Trick:

alias echo='{ set +x; } 2> /dev/null; builtin echo'

Wenn wir jetzt sagen echo "Message", rufen wir den Alias ​​auf, der nicht verfolgt wird. Der Alias ​​deaktiviert die Trace-Option und unterdrückt gleichzeitig die Trace-Meldung des setBefehls (mit der Technik, die zuerst inAntwort von user5071535) und führt dann den eigentlichen echoBefehl aus. Dadurch können wir einen ähnlichen Effekt wie in der Antwort von user5071535 erzielen, ohne den Code bearbeiten zu müssen.jeden echoBefehl. Allerdings bleibt der Trace-Modus dabei ausgeschaltet. Wir können kein set -xin den Alias ​​einfügen (oder zumindest nicht einfach), da ein Alias ​​nur das Ersetzen eines Wortes durch eine Zeichenfolge erlaubt; kein Teil der Alias-Zeichenfolge kann in den Befehl eingefügt werden. nachdie Argumente (z. B. "Message"). Wenn das Skript beispielsweise enthält

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date

Die Ausgabe wäre

+ date
Mon, Oct 31, 2016  0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016  0:00:04 AM           # Note that it doesn’t say  + date

Sie müssen die Trace-Option also immer noch nach der Anzeige der Nachricht(en) wieder einschalten — aber nur einmal nach jedemBlockaufeinanderfolgender echoBefehle:

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date


Es wäre nettset -xwenn wir die automatische nach einem machen könnten echo– und das können wir, mit ein bisschen mehr Trickserei. Aber bevor ich das vorstelle, bedenken Sie Folgendes. Der OP beginnt mit Skripten, die ein #!/bin/sh -exShebang verwenden. Implizit könnte der Benutzer das xaus dem Shebang entfernen und ein Skript haben, das normal funktioniert, ohne Ausführungsverfolgung. Es wäre schön, wenn wir eine Lösung entwickeln könnten, die diese Eigenschaft beibehält. Die ersten paar Antworten hier erfüllen diese Eigenschaft nicht, weil sie die Verfolgung nach Anweisungen bedingungslos „wieder“ einschalten echo, ohne Rücksicht darauf, ob sie bereits eingeschaltet war.  Diese Antworterkennt dieses Problem offensichtlich nicht an, da esersetzt echoAusgabe mit Trace-Ausgabe; daher verschwinden alle Nachrichten, wenn das Tracing ausgeschaltet wird. Ich werde jetzt eine Lösung vorstellen, die das Tracing nach einer echoAnweisung wieder einschaltetbedingt— nur wenn es bereits eingeschaltet war. Dies auf eine Lösung herunterzustufen, die das Tracing bedingungslos „wieder“ einschaltet, ist trivial und bleibt eine Übung.

alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
        builtin echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}

$-ist die Optionsliste; eine Verkettung der Buchstaben, die allen festgelegten Optionen entsprechen. Wenn beispielsweise die Optionen eund xfestgelegt sind, $-wird ein Buchstabensalat mit eund angezeigt x. Mein neuer Alias ​​(oben) speichert den Wert von , $-bevor die Ablaufverfolgung deaktiviert wird. Wenn die Ablaufverfolgung deaktiviert ist, übergibt er die Kontrolle an eine Shell-Funktion. Diese Funktion führt die eigentliche Operation aus echo und prüft dann, ob die xOption aktiviert war, als der Alias ​​aufgerufen wurde. Wenn die Option aktiviert war, aktiviert die Funktion sie wieder; wenn sie deaktiviert war, lässt die Funktion sie deaktiviert.

Sie können die obigen sieben Zeilen (acht, wenn Sie ein einfügen shopt) am Anfang des Skripts einfügen und den Rest so lassen, wie er ist.

Dies würde Ihnen ermöglichen

  1. um eine der folgenden Shebang-Zeilen zu verwenden:
    #!/bin/sh -ex
    #!/bin/sh -e
    #!/bin/sh –x
    oder einfach nur
    #!/bin/sh
    und es sollte wie erwartet funktionieren.
  2. Code haben wie
    (Kram)
    Befehl 1
    Befehl 2
    Befehl 3
    setze -x
    Befehl 4
    Befehl 5
    Befehl 6
    setze +x
    Befehl 7
    Befehl 8
    Befehl 9
    Und
    • Die Befehle 4, 5 und 6 werden verfolgt – es sei denn, einer davon ist ein echo. In diesem Fall wird er ausgeführt, aber nicht verfolgt. (Aber auch wenn Befehl 5 ein ist echo, wird Befehl 6 trotzdem verfolgt.)
    • Die Befehle 7, 8 und 9 werden nicht verfolgt. Auch wenn Befehl 8 ein ist echo, wird Befehl 9 trotzdem nicht verfolgt.
    • Die Befehle 1, 2 und 3 werden verfolgt (wie 4, 5 und 6) oder nicht (wie 7, 8 und 9), je nachdem, ob der Shebang enthält x.

builtinPS: Ich habe festgestellt, dass ich auf meinem System das Schlüsselwort in meiner mittleren Antwort (das nur ein Alias ​​für ist ) weglassen kann echo. Das ist nicht überraschend; bash(1) sagt, dass während der Aliaserweiterung …

… ein Wort, das mit einem zu erweiternden Alias ​​identisch ist, wird nicht ein zweites Mal erweitert. Das bedeutet, dass man einen Alias ​​verwenden kannlsZuls -F, und Bash versucht nicht, den Ersetzungstext rekursiv zu erweitern.

Wenig überraschend schlägt die letzte Antwort (die mit echo_and_restore) fehl, wenn das builtinSchlüsselwort 1 weggelassen wird . Aber seltsamerweise funktioniert es, wenn ich das lösche builtinund die Reihenfolge ändere:

echo_and_restore() {
        echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'

__________
1  Es scheint zu undefiniertem Verhalten zu führen. Ich habe gesehen

  • eine Endlosschleife (wahrscheinlich aufgrund unbegrenzter Rekursion),
  • eine /dev/null: Bad addressFehlermeldung und
  • ein Core Dump.

Antwort2

Ich habe eine Teillösung gefunden beiInformierenIT:

#!/bin/bash -ex
set +x; 
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"

Ausgänge

set +x; 
shell tracing is disabled here 
+ echo "but is enabled here"
but is enabled here

Leider hallt es immer noch set +x, aber danach ist immerhin Ruhe. Es ist also zumindest eine Teillösung des Problems.

Aber gibt es vielleicht einen besseren Weg, das zu tun? :)

Antwort3

set +xAuf diese Weise verbessern Sie Ihre eigene Lösung, indem Sie die Ausgabe entfernen :

#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"

Antwort4

Setzen Sie es set +xin Klammern, sodass es nur für den lokalen Bereich gilt.

Zum Beispiel:

#!/bin/bash -x
exec 3<> /dev/null
(echo foo1 $(set +x)) 2>&3
($(set +x) echo foo2) 2>&3
( set +x; echo foo3 ) 2>&3
true

würde ausgeben:

$ ./foo.sh 
+ exec
foo1
foo2
foo3
+ true

verwandte Informationen