So behalten Sie den letzten Beendigungsstatus nach dem Test bei

8

Ist es möglich, den Status des letzten Befehlsausgangs ( $?) nach einem Test unverändert zu lassen?

ZB möchte ich machen:

command -p sudo ...
[ $? -ne 1 ] && exit $?

Der letzte exit $?sollte den sudo-Exit-Status zurückgeben, gibt jedoch immer zurück 0(den Exit-Code des Tests).

Ist das ohne temporäre Variable möglich?

Ein weiteres Beispiel zur weiteren Verdeutlichung:

 spd-say "$@"
 [ $? -ne 127 ] && exit $?

In diesem Fall möchte ich nur beenden, wenn der erste Befehl gefunden wird (Exit-Code! = 127). Und ich möchte mit dem eigentlichen spd-sayExit-Code beenden (möglicherweise nicht 0).

EDIT: Ich habe vergessen zu erwähnen, dass ich eine POSIX-Beschwerdelösung für eine bessere Portabilität bevorzuge.

Ich verwende dieses Konstrukt in Skripten, in denen ich Alternativen für denselben Befehl bereitstellen möchte. Siehe zum Beispiel mein crc32-Skript .

Das Problem mit temporären Variablen besteht darin, dass sie andere Variablen beschatten können. Um zu vermeiden, dass Sie lange Namen verwenden müssen, ist dies für die Lesbarkeit des Codes nicht gut.

eadmaster
quelle
Nein, aber Sie können einfach das tun, if ! command -p sudo; then exit; fiwas für Ihr Beispiel die gleichen Ergebnisse hätte.
Jordan
ok, was ist, wenn ich stattdessen auf den 127-Code testen möchte? (zB [ $? -ne 127 ] && exit $?)
eadmaster
1
@ Jordanm: Wirklich? Wenn das OP den Code / die Logik präsentiert hat, den er beabsichtigt hat, und wenn ich ihn richtig lese, möchte er, dass das Skript beendet wird, wenn der sudoBefehl erfolgreich ist (dh wenn er sudomit dem Status 0 beendet wird). Aber in Ihrem Code läuft das Skript weiter (wird nicht beendet), wenn sudoes erfolgreich ist
G-Man sagt 'Reinstate Monica'
@ G-Man - Ich bin mir ziemlich sicher, dass die Bedingungen des Fragestellers tatsächlich auf der Rückkehr von commandund überhaupt nicht basieren sudo. Das ist gemeint mit Ich möchte nur beenden, wenn der erste Befehl gefunden wird (Exit-Code! = 127) und ist eine angegebene Rückgabe,command wenn der aufgerufene Befehl nicht gefunden wird. Ich denke, das Problem ist, dass das Aufrufen sudoals Teil des Tests es ermöglicht, sudodie Rückkehr von commandan erster Stelle zu quetschen und so den Test zu verzerren .
Mikesserv

Antworten:

3

$_arbeitet in wird (mindestens) interaktiv dash, bash, zsh, ksh (wenn auch offensichtlich nicht in einer bedingten Anweisung wie gewünscht) und mkshMuscheln. Von diesen - meines Wissens - nur bashund zshwird es auch in einer Skript-Shell füllen. Es ist kein POSIX-Parameter - aber für jede moderne, interaktive Shell ziemlich portabel.

Für eine tragbarere Lösung können Sie Folgendes tun:

command -p sudo ...
eval '[ "$?" = 127 ] || exit '"$?"

Dies ermöglicht es Ihnen grundsätzlich, den Anfangswert für $?bis zum Ende des Skripts zu erweitern, bevor Sie den Wert überhaupt an der Spitze testen.

Aber wie auch immer, da Sie scheinen zu testen , ob der Befehl sudokann builtin in der Schale ist zu finden -p tragbare Pfad mit einer Schnur command, würde ich denken , dass Sie es ein litte mehr direkt gehen könnten. Auch nur klar zu sein, command wird nicht für den Standort aller testen Argumente zu sudo- so ist es nur ist sudo- und nichts , um es ruft - die zu diesem Rückgabewert relevant ist.

Und wenn Sie das versuchen:

command -pv sudo >/dev/null || handle_it
command -p  sudo something or another

... würde als Test gut funktionieren, ohne dass Fehler in den Befehlsläufen auftreten, sudodie so zurückgegeben werden, dass die Ergebnisse Ihres Tests verzerrt werden.

mikeserv
quelle
9

Abhängig von den tatsächlichen Anforderungen gibt es verschiedene Optionen, um den Exit-Status zuverlässig und ohne Overhead zu handhaben.

Sie können den Exit-Status mithilfe einer Variablen speichern:

command -p sudo ...
rc=$?
[ "$rc" -ne 1 ] && echo "$rc"

Sie können Erfolg oder Misserfolg direkt überprüfen:

if  command -p sudo ...
then  echo success
else  echo failure
fi

Oder verwenden Sie ein caseKonstrukt, um den Exit-Status zu unterscheiden:

command -p sudo ...
case $? in
(1) ... ;;
(127) ... ;;
(*) echo $? ;;
esac

mit dem in der Frage gestellten Sonderfall:

command -p sudo ...
case $? in (1) :;; (*) echo $?;; esac

Alle diese Optionen haben den Vorteil, dass sie dem POSIX-Standard entsprechen.

(Hinweis: Zur Veranschaulichung habe ich die echoobigen Befehle verwendet. Ersetzen Sie sie durch entsprechende exitAnweisungen, um der angeforderten Funktion zu entsprechen.)

Janis
quelle
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Terdon
1
(Ich frage mich, warum der letzte Kommentar nicht zum Chat verschoben wurde.) - Wie bereits ausführlich erklärt und durch POSIX-Zitate in der Chat-Diskussion untermauert (siehe dort für Details); 1.) ("$(get_errnos)")ist eine künstliche Hinzufügung einer Befehlssubstitution, bei der Vergleiche mit tatsächlichen Fehlercodes in der Frage verlangt werden. 2.) In der Norm ist klar, was sich ändert $?(und was sich nicht ändert), und 3.) dort sind keine Nebenwirkungen mit diesem Konstrukt (es sei denn, Sie führen sie unnötig ein). - OTOH, da es Ihnen etwas ausmacht, ist die andere Antwort, die Sie bevorzugen, eindeutig nicht standardisiert.
Janis
1
@mikeserv; Es ist ärgerlich (und irreführend!) Nicht nur, weil Sie Doppelmoral anwenden ! Wenn Sie denselben künstlichen $(get_errnos)Code auf andere Lösungen anwenden würden ( ( exit 42 ); test "$(get_errnos)" -ne $? && echo $_), funktionieren diese ebenfalls nicht. (Sie haben es vorgezogen, meine Standardlösung in Fehlkrediten zu bringen , nicht die anderen nicht standardmäßigen Hack (s) .) - Natürlich können Sie allen Antworten beliebigen Code hinzufügen und ihn verderben. - Und WRT zur Änderung von $?; Nur weil du es nicht finden kannst, heißt das nicht, dass es nicht wahr ist. (Ich habe in unseren Diskussionen bereits die Schlüsselwörter angegeben, nach denen in POSIX gesucht werden soll.)
Janis
1
@mikeserv; Aber dies ist wiederum anfällig dafür, die (fruchtlose und endlose) Diskussion fortzusetzen, die hier nicht platziert werden sollte, wie @terdon richtig beobachtet.
Janis
1
@mikeserv; Ich habe ganz am Anfang gesagt, wo Sie zuerst erwähnt haben zsh(zuerst allein erwähnt; später haben Sie auch hinzugefügt dash), dass ich denke, dass es dort ein Fehler sein muss, da der caseExit-Status und die Einstellung von $?in POSIX gut definiert zu sein scheinen. - Und auch hier wenden Sie Doppelmoral an; Der andere Hack ist z. B. weder Standard noch funktioniert er zuverlässig in anderen Standard-Shells (z ksh. B. nicht in ). - Meine Vorschläge sind Standard und funktionieren in bash(meistens unter Linux verwendet) und ksh(der vorherrschenden Shell in kommerziellen Unixen).
Janis
7
command -p ...
test 1 -ne $? && exit $_

Use $_, das bis zum letzten Argument des vorherigen Befehls erweitert wird.

llua
quelle
1
Gültig für dieses Beispiel, aber nur verwendbar, wenn sich zwischen den Referenzen $?und kein anderer Befehl befindet $_. IMHO ist es besser, sich an eine konsistente Methode zu halten, die in anderen Fällen funktioniert (und auch die Lesbarkeit des Codes verbessern kann).
Dan Cornilescu
4
Die Shell wurde zweimal speziell benannt, einmal im Titel und einmal als Tag. Auch wurde Portabilität in der Frage nicht erwähnt, daher gab ich eine Antwort, die in dieser Shell funktioniert. Welche anderen seltsamen Einschränkungen werden erfunden?
llua
1
@mikeserv; Falls Sie es verpasst haben: Ich sagte: "Es gab hier einen ersten Kommentar zu POSIX." was korrigiert werden musste. Nicht mehr und nicht weniger. - Wie mit Ihnen ausführlich diskutiert und dort erklärt, sind alle drei Vorschläge in der anderen Antwort von POSIX gut definiert. Sie müssen hier Ihre (IMO-falsche) Meinung nicht wiederholen oder eine weitere Wiederholung des Streits beginnen.
Janis
1
@mikeserv; Die Expansionsnebenwirkungen in den case Mustern , der einzige Ort, der theoretisch von Bedeutung wäre (aber nicht in der gegebenen Frage), ist ein konstruierter Fall. - In jedem Fall würde Ihre Shell-Befehlsersetzung das Ergebnis des eingebetteten Befehls weitergeben. In der Regel wirken sich andere Erweiterungen ohnehin nicht auf den Rückgabestatus aus, wenn keine Befehle beteiligt sind, die Fehler verursachen können (Mind x=${a!b}Cases, aber hier irrelevant). - Was meinst du mit "Befehl ohne Befehlsname" ?
Janis
1
@mikeserv; Es kann nur durch Ad-hoc-Code überlastet werden, der unnötigen Code enthält. Es gibt absolut keine Grauzone, wenn Sie den Vorschlag annehmen, ohne unnötig künstlichen Unsinn einzuführen. Die Anforderungen waren in diesem Fall absolut klar: 1. Führen Sie einen Befehl aus, 2. Überprüfen Sie den Exit-Code, 3. Geben Sie den Exit-Code zurück. - Verstehst du das? - Sie haben diese Anforderung willkürlich geändert, um nur ein Argument zu bilden.
Janis
6

Sie können eine Shell-Funktion definieren (und verwenden):

check_exit_status()
{
    [ "$1" -ne 1 ] && exit "$1"
}

Dann

command -p sudo ...
check_exit_status "$?"

Dies ist wohl "Betrug", da es eine Kopie $?in der check_exit_statusArgumentliste erstellt.

Dies mag etwas umständlich erscheinen, und das ist es auch. (Das passiert manchmal, wenn Sie Problemen willkürliche Einschränkungen auferlegen.) Dies mag unflexibel erscheinen, ist es aber nicht. Sie können check_exit_statuskomplexer gestalten, indem Sie Argumente hinzufügen, die angeben, welche Tests für den Exit-Statuswert durchzuführen sind.

Scott
quelle
0

Um Ihre direkte Frage zu beantworten, nein, es ist nicht möglich, $?unverändert zu bleiben . Und dies ist einer der Fälle, in denen Sie sich vermutlich auf das falsche Problem konzentrieren. Eine temporäre Variable ist der Standard- und bevorzugte Weg, um den gewünschten Effekt zu erzielen. Es ist so normal, dass ich vorschlagen würde, den Grund aufzugeben (oder zu überdenken), von dem Sie glauben, dass Sie ihn nicht verwenden möchten. Ich bezweifle sehr, dass es die zusätzliche Komplexität wert ist, die durch jede andere Methode hinzugefügt wird.

Wenn Sie nur aus einfacher Neugier fragen, lautet die Antwort nein.

David Z.
quelle
@mikeserv Ich bin mir nicht sicher, ob ich das verstehe. Sprechen Sie über die manuelle Zuweisung $?oder so etwas?
David Z
0

Hier ist mein Code-Snippet, um einen vorherigen Exit-Status beizubehalten, ohne das aktuelle Skript / die aktuelle Shell zu verlassen

EXIT_STATUS=1
if [[ $EXIT_STATUS == 0 ]]
then
  echo yes
else
  (exit $EXIT_STATUS)
fi
Junaid
quelle