Protokoll-Exit-Befehlscode, ähnlich dem Zeitbefehl

10

mit

time sleep 1

Ausbeuten:

$ time sleep 1

real    0m1.005s
user    0m0.001s
sys     0m0.001s

Gibt es einen Befehl, mit dem ich den Exit-Code sleepoder einen beliebigen Befehl drucken kann, den ich ausführen möchte?

Etwas mag:

$ log-exit-code sleep 1

vielleicht reicht das aus?

sleep 1 && echo "$?"
Alexander Mills
quelle
3
Nein, sleep 1 && echo $?würde den Code der Schlafzelle nur drucken, wenn er Null ist ...
Satō Katsura
oh lol du hast recht
Alexander Mills
1
sleep 1 && echo "$?" || echo "$?"
Jesse_b
2
Ich habe eine Antwort hinzugefügt, da alle anderen bisher den ursprünglichen Rückgabewert verwerfen, was imo schlecht ist (nach diesen Antworten ist $? Immer 0, da das letzte, was sie tun, eine Anzeige ist, die immer funktioniert. Sie alle haben vergessen, sie beizubehalten den ursprünglichen Rückgabewert und geben Sie ihn zurück, nachdem Sie ihn angezeigt haben. Daher zeigen sie ihn nur einem Beobachter an, verbergen ihn jedoch für den Rest des Skripts, der jetzt nicht "$?" verwenden kann, um zu sehen, ob dieser wichtige Befehl zurückgegeben wird 0 oder etwas anderes ...)
Olivier Dulac
1
Alle folgenden Lösungen können diese Funktionalität problemlos implementieren. Kusalanandas zum Beispiel wäre nur:EXIT_CODE=$(tellexit command)
Jesse_b

Antworten:

4

In meinen bisherigen Tests hat dies funktioniert:

command && echo "$?" || echo "$?"

Weist es einfach an, den Exit-Code zu wiederholen, wenn er erfolgreich ist oder fehlschlägt.

Wie Sato weiter unten ausführte, ist dies im Wesentlichen dasselbe wie:

command; echo "$?"

Eine Sache, die das und / oder den Befehl lohnenswert machen könnte, ist so etwas wie:

command && echo "Success! Exit Code: $?" || echo "Failure! Exit Code: $?"

Wenn Sie Ihr Skript benötigen, um auf den Exit-Code zu reagieren, wie es Olivier betrifft, ist dies kein Problem. Ihr Skript könnte ungefähr so ​​aussehen:

command
case "$?" in; 
    0) echo "Command exited with: 0"
       <command if good>
       ;;
    1) echo "Command exited with: 1"
        <command if bad>
        ;;
    255) echo "Command exited with: 255"  # for ssh maybe?
         <command if 255>
         ;;
    *) echo "Command exited with: >2"
        <command for other exit code>
        ;;
esac
jesse_b
quelle
13
Äh, wie ist das besser als nur command; echo $??
Satō Katsura
4
Ich denke es ist nicht. command; echo $?ist eine viel bessere Antwort.
Jesse_b
uhhh das scheint die beste Option zu sein, also habe ich es als Antwort gewählt, wenn jemand anderer Meinung ist, lmk
Alexander Mills
@ AlexanderMills Nur Sie wissen, welche Antwort auf Ihre Frage am besten geeignet ist :-)
Kusalananda
1
Ich mag das nicht: Dies ändert den Exit-Code nach, command ; echo $?weil echo $?immer erfolgreich ist und daher danach der neue Wert von $? ist jetzt 0. Es gibt eine Möglichkeit, den ursprünglichen Rückkehrcode auch nach der Anzeige beizubehalten, falls dieser ursprüngliche Rückkehrcode später nützlich ist (Beispiel: In einem Skript ist es wichtig, den Rückkehrcode zu überprüfen, bevor Sie entscheiden, wie Sie fortfahren möchten). Siehe meine Antwort für eine von mehreren Lösungen (die meisten Antworten hier können auf die gleiche Weise geändert werden)
Olivier Dulac
11

cmd && echo "$?"würde nicht funktionieren, da es notwendigerweise nur Nullen drucken echowürde (die würden nur nach erfolgreichem Abschluss des vorhergehenden Befehls ausgeführt).

Hier ist eine kurze Shell-Funktion für Sie:

tellexit () {
    "$@"

    local err="$?"
    printf 'exit code\t%d\n' "$err" >/dev/tty
    return "$err"
}

Dadurch wird der Exit-Code des angegebenen Befehls auf ähnliche Weise wie beim timeBefehl gedruckt.

$ tellexit echo "hello world"
hello world
exit code       0

$ tellexit false
exit code       1

Durch Umleiten des printfto /dev/ttyin die Funktion können wir weiterhin tellexitUmleitungen verwenden, ohne dass Junk in unseren Standardausgabe- oder Fehlerströmen auftritt:

$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code       0
$ cat out
hello
$ cat err
world

Durch Speichern des Exit-Codes in einer Variablen können wir ihn an den Aufrufer zurückgeben:

$ tellexit false || echo 'failed'
exit code       1
failed

Eine schickere Version derselben Funktion gibt auch das Signal aus, das den Befehl beendet hat, wenn der Exit-Code größer als 128 ist (was bedeutet, dass er aufgrund eines Signals beendet wurde):

tellexit () {
    "$@"

    local err="$?"

    if [ "$err" -gt 128 ]; then
        printf 'exit code\t%d (%s)\n' "$err" "$(kill -l "$err")" >/dev/tty
    else
        printf 'exit code\t%d\n' "$err" >/dev/tty
    fi

    return "$err"
}

Testen:

$ tellexit sh -c 'kill $$'
exit code       143 (TERM)

$ tellexit sh -c 'kill -9 $$'
Killed
exit code       137 (KILL)

(Das localDing erfordert ash/ pdksh/ bash/ zsh, oder Sie können es ändern, typesetwas auch einige andere Muscheln verstehen.)

Kusalananda
quelle
cool, wie würden Sie tellexit verwenden, zeigen Sie es in Aktion pls
Alexander Mills
1
@AlexanderMills Siehe Update.
Kusalananda
2
Es sollte den ursprünglichen Rückgabewert beibehalten und am Ende zurückgeben.
Olivier Dulac
@OlivierDulac Wirklich gute Beobachtung! Ich werde ändern.
Kusalananda
7

Verwenden Sie eine Shell-Wrapper-Funktion. Wahrscheinlich mit einem anderen Namen.

$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"      
test test
0
$ 

(Dies wird natürlich die Standardausgabe ttybeschädigen. Verwenden Sie daher entweder die von @Kusalananda gezeigte oder nicht außerhalb interaktiver Kontexte.)

Auf dem Weg in ein nicht portierbares Gebiet können einige Shells den Status aller Befehle in einer Pipeline melden, nicht nur den letzten, z. B. in ZSH, wenn Fehler aus einer gesamten Pipeline gemeldet werden sollen:

% TRAPZERR() { print >/dev/tty $pipestatus }
% perl -e 'exit 42' | perl -e 'exit 99'
42 99
% false | perl -e 'exit 42' | perl -e 'exit 99'
1 42 99
% perl -e 'exit 42' | perl -e 'exit 99' | true
% 

TRAPZERR Andernfalls wird nicht ausgelöst, wenn kein Fehler vorliegt (im Prinzip "Keine Nachrichten sind gute Nachrichten").

Thrig
quelle
1
Es sollte den ursprünglichen Rückgabewert beibehalten und am Ende zurückgeben. Das Anzeigen (printf, echo) einer einfachen Sache (beginnt nicht mit einem '-' usw.) funktioniert immer und danach wird "$?" hat jetzt den Wert "0" als die Anzeige funktionierte
Olivier Dulac
7

GNU timehat hierfür eine Option:

time -f %x sleep 1
0

Durchläuft den Exit-Code 1 , sofern er nicht durch ein Signal 2 getötet wird :

$ /usr/bin/time -f %x sleep 1; echo $?
0
0

$ /usr/bin/time -f %x sleep 2x; echo $?
sleep: invalid time interval ‘2x’
Try 'sleep --help' for more information.
1
1

$ /usr/bin/time -f %x sleep 60s; echo $? # Press ^C before a minute elapses
0
2

Wenn Sie die Signal-Kill-Situation kennen / behandeln möchten, übergeben Sie -vden stderr und suchen Sie nach dem String Command terminated by signal.


1 Vielen Dank an Olivier Dulac für die Notiz, dass der Exit-Code durchläuft.
2 Vielen Dank auch an Stéphane Chazelas für den Hinweis, dass der Exit-Code für das Kill-Signal nicht durchgeht.

Bischof
quelle
1
Interessant. Nur GNU, aber interessant, da dieser (wahrscheinlich) den ursprünglichen Rückkehrcode für den Rest des folgenden Codes beibehalten wird (dh er ersetzt ihn nicht durch "0" wie die anderen Antworten)
Olivier Dulac
2
Es wird 0jedoch zurückgegeben, wenn der Befehl beendet wird.
Stéphane Chazelas
2
@ Bischof, ich stimme zu 0 in diesen Fällen ist weniger als ideal. Beachten Sie jedoch, dass es keine allgemeine Übereinstimmung darüber gibt, was zu melden ist, wenn ein Befehl durch ein Signal getötet wird. Die Rückgabe der Signalnummer 128 + wird von einigen Shells verwendet. Sie sehen auch Dinge wie 256+signumoder 384+signumoder sogar signum/256oder signum/256+0.5ob ein Kern generiert wurde (tatsächlich wird der Status durch waitpid()dividiert durch 256 zurückgegeben). Einige würden eine Textdarstellung geben (wie sigintoder sigquit+core). Es lohnt sich wahrscheinlich immer noch, in der Newsgroup gnu.coreutils.bugs zu diskutieren.
Stéphane Chazelas
3
(ist eigentlich timenicht Teil von coreutils, es ist ein Paket für sich mit einer eigenen Bug-Mailing-Liste ([email protected]))
Stéphane Chazelas
1
@bishop, die system()von traditionellen awks wie die, die noch von Brian Kernighan (the kin awk) gepflegt werden, oder die nawkvon Solaris, die awkvon FreeBSD, die sich daraus ableiten. awk 'BEGIN{print system("kill -s ABRT $$")}'Wird ausgegeben, 0.523438wenn die Größe des Core-Dumps nicht begrenzt ist. Das Verhalten von gawkhat sich in letzter Zeit geändert, mit unterschiedlichem Verhalten mit --traditionaloder --posix(siehe auch den Rückgabewert von close(cmd)für weitere Variationen)
Stéphane Chazelas
2

Ich mag alle anderen Antworten nicht (obwohl ich ihre Klugheit sehr mag): Sie zeigen den Exit-Code an, aber sie ÄNDERN ihn auch. (Der anzeigende Teil ist wahr, daher ist der Rückkehrcode danach 0)

Hier ist eine modifizierte Version:

do_and_tell () {
   "$@"
   returncode="$?"
   printf "Execution of : \n%s\n yelded a return code of: \n%s\n" "$*" "$returncode"
   return $returncode  # the interresting addition... keeps the commands return value.
}

## usage:

prompt$ do_and_tell true
Execution of : 
true
 yelded a return code of: 
0

prompt$ echo $?
0

prompt$ do_and_tell false
Execution of : 
false
 yelded a return code of: 
1

prompt$ echo $?
1
Olivier Dulac
quelle
Tatsächlich ist die Antwort von @bishop die einzige andere, die diesen Wert beibehält, sich jedoch auf die Version von GNU stützt, timedie nicht immer verfügbar ist.
Olivier Dulac
2

Um etwas robuster zu sein, senden Sie den Exit-Status an einen separaten FD, damit er unabhängig von stdout / stderr behandelt werden kann:

exit_status() {
    "$@"
    rv=$?
    echo >&3 "$rv"
    return "$rv"
}

# exit status and stdout to stdout:
> exit_status echo "hello" 3>&1
hello
0

# exit_status to stdout, stdout to /dev/null
> exit_status echo "hello" 3>&1 1>/dev/null
0
> exit_status ehco "hello" 3>&1 1>/dev/null
ehco: command not found
127

# exit_status to stdout, stdout and stderr to /dev/null
> exit_status ehco "hello" 3>&1 &>/dev/null
127

Beachten Sie, dass Sie etwas mit FD3 tun müssen, sonst wird es sagen Bad file descriptor.

Dies könnte weiter konfiguriert werden, um diese Ausgänge für Eingänge eines anderen Programms bereitzustellen, indem das andere Programm mehr als einen FD abhört; Sie könnten dies als eine Art Kibana der frühen 90er verwenden :)

onwsk8r
quelle
Es sollte den ursprünglichen Rückgabewert beibehalten und am Ende zurückgeben. Das Anzeigen (printf, echo) einer einfachen Sache (beginnt nicht mit einem '-' usw.) funktioniert immer und danach wird "$?" hat jetzt den Wert "0" als die Anzeige funktionierte
Olivier Dulac
1

Während alle anderen (sehr interessanten) Antworten genau die Frage betreffen, die das OP gestellt hat, sind die Dinge in der Praxis normalerweise einfacher. Sie speichern den Exit-Status einfach in einer Variablen (unmittelbar nach dem Befehl) und melden oder protokollieren ihn nach Ihren Wünschen. ZB in / dev / stderr drucken oder als Textnachricht in eine Protokolldatei schreiben / anhängen. Außerdem wird es für die Entscheidungsfindung / Flusskontrolle im nachfolgenden Code beibehalten.

Wenn es in einer Funktion ist:

function some-function () {
    local rc ## to avoid side effects
    ...
    some-command ## run any command
    rc=$?  ## save the return status in a variable
    echo "some-command returned $rc" ## optionally tell the user about it
    ...
    return $rc  ## at the end, if appropriate
}

Wenn es direkt in einem Skript ist:

some-command ## run any command
rc=$?  ## save the return status in a variable
echo "some-command returned $rc" ## optionally tell the user about it
...
exit $rc    ## at the end to tell any calling script about it, if appropriate

(Ducken und Bedecken, da dies nicht die genaue Frage beantwortet .)

Joe
quelle