2012-10-05 7 views
7

Ich habe durch einige der ähnlichen Fragen (wie How to set a variable to the output from a command in Bash?), aber die angenommenen Antworten scheinen nicht für mich arbeiten. Ich war mir nicht sicher, ob ich die Frage eines anderen entgleisen oder mein eigenes Duplikat veröffentlichen sollte, also entschuldige mich bitte, wenn ich hier falsch gewählt habe.Wie bekomme ich die Ausgabe eines Bash-Befehls in einer Variablen

Ich möchte den Ausgang und den Exit-Status einer Reihe von Befehlen in einem Skript erhalten, das ich zusammenstelle. Hier ist ein Beispiel dafür, was ich unter Verwendung worden:

cmd_output=$(rm $file) 
exit_status=$? 
if [ "${exit_status}" -eq 0 ] 
then 
log "Successfully removed the original" ${TAB_LEVEL} 
else 
fail "Failed to remove the original, the output was: \n ${cmd_output}" 
fi 

Das Protokoll und fail-Funktionen sind:

# Usage: fail "Failure message" 
function fail { 
echo "FATAL ERROR: $1" >> "${LOG_DIR}/${LOG_FILE}" 
exit 1 
} 

# Usage: log "Log message" 3 Where the tab-level is 3. 
function log { 
if (("${2}" > 0)) 
then 
eval "printf ' %.0s' {1..$2}" >> "${LOG_DIR}/${LOG_FILE}" 
fi 
echo "$1" >> "${LOG_DIR}/${LOG_FILE}" 
return 0 
} 

In dem obigen Beispiel ich die $ (cmd) Format verwenden, aber ich habe auch versucht, Backticks verwenden.

In meiner Protokolldatei, alles was ich sehe, wenn es ein Fehler ist:

FATAL ERROR: Failed to remove the original, the output was: \n

Auch endet die Ausgabe der ausgefallenen Befehle auf dem Bildschirm nach oben wie üblich. Gibt es einen allgemeinen Grund, dass meine cmd_output-Variablen leer bleiben würden?

Antwort

22

Sie haben die Ausgabe des speziellen Standardfehlerausgabe-Stream eingefügt:

cmd_output=$(rm "$file" 2>&1) 

Es gibt drei Standardströme auf jedem Programm (die Datei-Deskriptoren nummeriert sind):

0. Standard input (where the program normally reads from) 
1. Standard output (where the program normally writes to) 
2. Standard error (where the program normally writes error messages) 

So zu Erfassen Sie die Fehlermeldungen, müssen wir die Standardfehlerausgabe (stderr) in die normale Standardausgabe (stdout) umleiten, die dann vom Ausdruck $(...) erfasst wird.

Die Syntax für die Umleitung lautet > "operator". Unmittelbar davor sagen Sie, welcher Dateideskriptor umgeleitet werden soll (der Standardwert ist 1, also stdout). Und Sie können es angeben, um in eine Datei umzuleiten. Wenn Sie danach ein kaufmännisches Und (&) schreiben, erzwingen Sie, dass es in einen anderen Dateideskriptor umgeleitet wird. Daher leiten wir in diesem Beispiel den Dateideskriptor 2 (stderr) in den Dateideskriptor 1 (stdout) um.

Sie können die Eingabe auch mit < "operator" umleiten, aber in diesem Fall ist der Standard-Dateideskriptor 0 (stdin).

Eine andere Beobachtung ist, dass es wahrscheinlich gute Praxis ist, Ihre $file Variable zwischen doppelte Anführungszeichen zu setzen, falls es Leerzeichen hat.

Hope this hilft ein wenig =)

+1

Froh, Sie über die Marke von 1000 zu schieben, schreiben Sie weiter! – shellter

+0

Hey Janito, vielen Dank für deine Antwort! Ich kenne die drei Standard-Streams und Redirection-Operatoren, aber der einzige Grund, warum ich sie bisher verwendet habe, ist, entweder alles von einem Befehl an/dev/null oder an eine Protokolldatei zu senden. Ich habe hier nicht einmal die verschiedenen Ströme betrachtet. Die $ file-Variable war ursprünglich in Anführungszeichen, ich habe sie aus Anführungszeichen genommen, als ich die Fehler auffindende Funktionen meines Skripts getestet habe, um einen Fehler zu erzwingen. Nochmals vielen Dank! –

5

* nichts Befehl hat in der Regel zwei Ausgabeformen: Standardausgabe (stdout) und Standardfehler (stderr).

FOO=$(...) erfasst nur stdout und lässt stderr ungehindert.

Wenn Sie die Inhalte von stderr mit dieser Syntax wollen, müssen Sie mit 2>&1 Ihren Befehl postfix so dass stderr in stdout verschmolzen wird. (z.B.etwa so: rm $file 2>&1)

+0

Hallo Antak, wollte nur ein paar Dankeschön für Ihre Antwort sagen. Da Janitos ein wenig detaillierter ist, verlasse ich es als die akzeptierte Antwort, aber ich schätze Ihre Hilfe sehr. –

0

Da Ihre fail Funktion nur Verlassen wird, wäre es viel einfacher sein, einfach tun:

set -e # Abort on failure 
exec 2>> "${LOG_DIR}/${LOG_FILE}" # Append all errors to LOG_FILE 
if cmd_output=$(rm $file) 
    log "Successfully removed the original" ${TAB_LEVEL} 
fi 

Der einzige Unterschied zwischen diesem und dem ursprünglichen Code ist, dass es nicht Druck tut der Text FATAL ERROR:. Da Kürze eine Tugend ist, würde es wahrscheinlich besser sein, die log Funktion vollständig zu überspringen. Fehler laut melden; gelingen still.

+0

Danke William. In diesem Fall möchte ich nicht stillschweigend erfolgreich sein, aber wenn ich ein Paket für den Vertrieb oder die meisten anderen Zwecke schreiben würde, würde ich zustimmen. Dies ist eine Inhouse-Software, und genauso wichtig wie das Verschieben von Dateien ist, dass sie sehr klare Details darüber enthält, was berührt wurde (vieles, was bewegt wird, sind sensible oder kritische Daten). Ich brauche auch das Präfix FATAL ERROR - die Protokolldatei wird von der Maschine durchlaufen, auf die die Daten kopiert werden, um nach dem oder einigen anderen Zeilenpräfixen zu suchen. Danke nochmal! –

Verwandte Themen