Ich arbeite an einem Shell-Skript, das einen komplexen Befehl aus Variablen erstellt, z. B. wie folgt (mit einer Technik, die ich aus den Bash-FAQ gelernt habe ):
#!/bin/bash
SOME_ARG="abc"
ANOTHER_ARG="def"
some_complex_command \
${SOME_ARG:+--do-something "$SOME_ARG"} \
${ANOTHER_ARG:+--with "$ANOTHER_ARG"}
Dieses Skript fügt die Parameter dynamisch hinzu --do-something "$SOME_ARG"
und gibt --with "$ANOTHER_ARG"
an, some_complex_command
ob diese Variablen definiert sind. Bisher funktioniert das gut.
Jetzt möchte ich den Befehl aber auch drucken oder protokollieren können, wenn ich ihn ausführe, beispielsweise wenn mein Skript in einem Debug-Modus ausgeführt wird. Wenn mein Skript ausgeführt wird some_complex_command --do-something abc --with def
, möchte ich diesen Befehl auch in einer Variablen haben, damit ich ihn z. B. im Syslog protokollieren kann.
In den Bash-FAQ wird eine Technik demonstriert, mit der der DEBUG
Trap und die $BASH_COMMAND
Variable (z. B. für Debugging-Zwecke) für diesen Zweck verwendet werden. Ich habe das mit folgendem Code versucht:
#!/bin/bash
ARG="test string"
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG"
echo "Command was: ${COMMAND}"
Dies funktioniert, erweitert jedoch nicht die Variablen im Befehl:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Ich denke , ich habe eval verwenden zu erweitern , echo "$ARG"
um echo test string
(zumindest habe ich nicht einen Weg ohne gefunden eval
noch) nicht . Folgendes funktioniert:
eval echo "Command was: ${COMMAND}"
Es wird die folgende Ausgabe erzeugt:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Command was: echo test string
Aber ich bin mir nicht sicher, ob ich so eval
sicher arbeiten kann . Ich habe erfolglos versucht, einige Dinge auszunutzen:
#!/bin/bash
ARG="test string; touch /x"
DANGER='$(touch /y; cat /etc/shadow)'
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG" $DANGER
echo "Command was: ${COMMAND}"
eval echo "Command was: ${COMMAND}"
Es scheint gut damit umzugehen, aber ich bin neugierig, ob jemand anderes ein Problem sieht, das ich verpasst habe.
Antworten:
Eine Möglichkeit besteht darin, eine Wrapper-Funktion zu erstellen, die gleichzeitig den Befehl druckt und wie folgt ausführt:
Damit Sie in Ihrem Skript Folgendes tun können:
Sie müssen nicht an der Falle herumspielen. Der Nachteil ist, dass einige
debug
Schlüsselwörter im gesamten Code hinzugefügt werden (aber das sollte in Ordnung sein, es ist üblich, solche Dinge wie Asserts usw. zu haben).Sie können sogar eine globale Variable hinzufügen
DEBUG
und diedebug
Funktion folgendermaßen ändern :Dann können Sie Ihr Skript wie folgt aufrufen:
oder
oder nur
abhängig davon, ob Sie die Debug-Informationen haben möchten oder nicht.
Ich habe die
DEBUG
Variable groß geschrieben , weil sie als Umgebungsvariable behandelt werden sollte.DEBUG
ist ein trivialer und gebräuchlicher Name, daher kann dies mit anderen Befehlen kollidieren. Nennen Sie es vielleichtGNIOURF_DEBUG
oderMARTIN_VON_WITTICH_DEBUG
oderUNICORN_DEBUG
wenn Sie Einhörner mögen (und dann mögen Sie wahrscheinlich auch Ponys).Hinweis. In der
debug
Funktion habe ich jedes Argument sorgfältig mit formatiert,printf '%q'
damit die Ausgabe korrekt maskiert und in Anführungszeichen gesetzt wird, damit sie wörtlich mit direktem Kopieren und Einfügen wiederverwendet werden kann. Es zeigt Ihnen auch genau, was die Shell gesehen hat, da Sie jedes Argument herausfinden können (bei Leerzeichen oder anderen lustigen Symbolen). Diese Funktion verwendet auch die direkte Zuordnung mit dem-v
Schalter vonprintf
, um unnötige Unterschalen zu vermeiden.quelle
&
. Ich musste diese auf die Funktion verschieben, damit sie funktioniert - nicht sehr schön, aber ich glaube nicht, dass es einen besseren Weg gibt. Aber nicht mehreval
, also habe ich das für mich, was schön ist :)eval "$BASH_COMMAND"
führt den Befehl aus.printf '%s\n' "$BASH_COMMAND"
Gibt den genau angegebenen Befehl sowie eine neue Zeile aus.Wenn der Befehl Variablen enthält (dh wenn es sich um etwas Ähnliches handelt
cat "$foo"
), wird beim Ausdrucken des Befehls der Variablentext ausgedruckt. Es ist unmöglich, den Wert der Variablen zu drucken, ohne den Befehl auszuführen - denken Sie an Befehle wievariable=$(some_function) other_variable=$variable
.Der einfachste Weg, einen Trace von der Ausführung eines Shell-Skripts zu erhalten, besteht darin, die
xtrace
Shell-Option festzulegen, indem Sie das Skript als ausführenbash -x /path/to/script
oderset -x
innerhalb der Shell aufrufen . Die Ablaufverfolgung wird auf Standardfehler gedruckt.quelle
xtrace
, aber das gibt mir nicht viel Kontrolle. Ich habe "set -x; ...; set + x" versucht, aber: 1) Der Befehl "set + x" zum Deaktivieren von xtrace wird ebenfalls gedruckt. 2) Ich kann der Ausgabe kein Präfix voranstellen, z. B. mit einem Zeitstempel. 3) Ich kann Nicht in Syslog protokollieren.BASH_COMMAND
, die den erweiterten Befehl enthält, da der Befehl irgendwann ohnehin eine Variablenerweiterung durchführen muss, wenn er ausgeführt wird :)