So erhalten Sie sowohl PIPESTATUS als auch die Ausgabe im Bash-Skript

9

Ich versuche mit diesem Befehl das letzte Änderungsdatum einer Datei abzurufen

TM_LOCAL=`ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'`

TM_LOCAL hat nach Ausführung dieser Zeile den Wert "2012-05-16 23:18"

Ich möchte auch PIPESTATUS überprüfen, um festzustellen, ob ein Fehler aufgetreten ist. Wenn die Datei beispielsweise nicht vorhanden ist, wird ls2 zurückgegeben. Sie $?hat jedoch den Wert 0, da sie den Rückgabewert von hat awk.

Wenn ich diesen Befehl alleine ausführe, kann ich den Rückgabewert von ls anhand von überprüfen ${PIPESTATUS[0]}

ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'

Funktioniert aber $PIPESTATUSnicht wie erwartet, wenn ich die Ausgabe wie im ersten Beispiel einer Variablen zuordne. In diesem Fall hat das $PIPESTATUSArray nur 1 Element, das dem entspricht$?

Die Frage ist also, wie ich beides bekommen $PIPESTATUSund die Ausgabe gleichzeitig einer Variablen zuweisen kann.

Mustafa Serdar Şanlı
quelle

Antworten:

8

Sie könnten dies tun:

TM_LOCAL=$(ls -l --time-style=long-iso ~/.vimrc | \
             awk '{ print $6" "$7 }' ; exit ${PIPESTATUS[0]} )

Dann $?wird der Rückkehrcode von ls. Dies funktioniert nicht, wenn Sie den Rückkehrcode von mehr als einem der Rohrteile benötigen (Sie können die Pipeline jedoch aufteilen, wenn die Ausgabe nicht zu groß ist, wie hier).

Hier ist eine ziemlich teure Möglichkeit, das gesamte PIPESTATUSArray und die Ausgabe zu erhalten. Nicht sehr elegant, aber nichts anderes gefunden:

result=$(echo -e "a\nb\nc" | \
          ( cat ; exit 1 ) | \
          ( cat ; exit 42 ) ; echo ${PIPESTATUS[@]})
output=$(head -n -1 <<< "$result")
status=($(tail -n 1 <<< "$result"))
echo "Output:"
echo "$output"
echo "Results:"
echo "${status[@]}"

Welches gibt:

Output:
a
b
c
Results:
0 1 42
Matte
quelle
Dies funktioniert in meinem Fall, aber ich bin immer noch gespannt, ob es eine Möglichkeit gibt, ein vollständiges Pipestatus-Array und die Ausgabe zu erhalten.
Mustafa Serdar Şanlı
3

Verwenden Sie set -o pipefailin bash, um den am weitesten rechts liegenden Exit-Code ungleich Null in einer Pipeline-Befehlssequenz als abzurufen $?. Von man bash:

Wenn festgelegt, ist der Rückgabewert einer Pipeline der Wert des letzten (am weitesten rechts stehenden) Befehls, der mit einem Status ungleich Null beendet wird, oder Null, wenn alle Befehle in der Pipeline erfolgreich beendet wurden. Diese Option ist standardmäßig deaktiviert.

Dann können Sie einfach darauf zugreifen $?. Verwenden Sie set +o pipefailwieder zu deaktivieren.

Daniel Beck
quelle
2

Ich gehe davon aus, dass das Problem hier ist, dass PIPESTATUS vollständig verschwindet, sobald Sie einen Befehl ausführen. Sie können das komplette PIPESTATUS-Array in Bash-Version 2 oder höher folgendermaßen erhalten:

declare -a status
status=(${PIPESTATUS[@]})

Dann Zugang ${status[0]}, ${status[1]}etc.

eewanco
quelle
2

Das Hauptproblem bei "Was Sie erwarten" ist, dass ein Befehl in Backquotes in einer Subshell ausgeführt wird. $PIPESTATUSexistiert dort und der von mir zurückgegebene Status folgt den gleichen Regeln, als ob Sie eine einzelne ausführbare Datei (oder ein Shell-Skript) ausgeführt hätten. Der Status des Befehls backquote ist der awkStatus ganz rechts ( ).

Um zu implementieren, was @ Daniel Beck gesagt hat, setzen Sie die pipefailOption in der Subshell folgendermaßen:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` Jetzt ist der Status, der $?danach gespeichert wird, der Status von ls(wenn nicht Null).

Ich denke jedoch, dass ein expliziter if [ -f ~/.vimrc ];... Test besser lesbar wäre.

Sie können keine Ausgabe in eine Variable erhalten und PIPESTATUSohne eine temporäre Datei für die erstere zurückgeben oder die letztere in eine Zeichenfolge umwandeln.

toddkaufmann
quelle
0

Ich wollte nur dann eine E-Mail von cron senden, wenn der Exit-Status nicht Null war

Der Trick ist, dass Sie, um das Standard für das Ende der Pipeline zu erhalten, es in eine Subshell einfügen müssen - aber das scheint den PIPESTATUS-Wert zu verbergen ...

Test Cron spuckt etwas Ausgabe aus und beendet mit 1 oder 0 ..

./testcron | (test ${PIPESTATUS[0]} -ne 0 || mail -s "testcron output" paul)

UPDATE: Der PIPESTATUS ist erst sichtbar, wenn der Pipeline-Befehl verarbeitet wird

Paul Davey
quelle
0

Eine Möglichkeit besteht darin, die Existenz Ihrer Datei zu überprüfen, bevor Sie die Änderungszeit mit einem Aufruf von erhalten stat. Da statim Zeitstempel etwas mehr zurückgegeben wird, als Sie möchten, können Sie ihn mithilfe der Parametererweiterung zuschneiden.

Mit GNU stat(z. B. unter Linux) können Sie Folgendes ausführen:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -c '%y' ~/.vimrc 2>/dev/null)
TM_LOCAL=${TM_LOCAL%:*}  # Safe to do, even if stat fails

Unter Mac OS X und anderen BSD-Systemen unterscheidet sich die statSyntax und kann ein Zeitformat angeben:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)
chepner
quelle
In der heutigen GNU-Antwort sagen Sie, dass die Änderung zu $TM_LOCALsicher ist. Es ist nur dann sicher, wenn Sie erwartet haben, dass es keinen vorherigen Wert hat. Angenommen, der Wert war zuvor 2020-02-27 17:14und es gibt keine ~/.vimrcDatei. Das hättest du dann 2020-02-27 17. Ich würde diese beiden Zeilen daher mit einer zusätzlichen &&Zeilengruppe verketten oder (vorzugsweise, da dies nicht so gut lesbar ist) eine ifZeilengruppe verwenden.
Adam Katz