Comment conserver le dernier état de sortie après le test

8

Est-il possible de conserver le dernier état de sortie de commande ( $?) inchangé après un test?

Par exemple, je voudrais faire:

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

Le dernier exit $?doit retourner le statut de sortie sudo, mais à la place il retourne toujours 0(le code de sortie du test).

Est-il possible de le faire sans variable temporaire?

Un autre exemple pour clarifier davantage:

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

Dans ce cas, je veux quitter uniquement si la première commande est trouvée (code de sortie! = 127). Et je veux quitter avec le spd-saycode de sortie réel (ce n'est peut-être pas le cas 0).

EDIT: J'ai oublié de mentionner que je préfère une solution de réclamation POSIX pour une meilleure portabilité.

J'utilise cette construction dans des scripts où je veux fournir des alternatives pour la même commande. Par exemple, consultez mon script crc32 .

Le problème avec les variables temporaires est qu'elles peuvent masquer d'autres variables et pour éviter que vous ne deviez utiliser des noms longs, ce qui n'est pas bon pour la lisibilité du code.

eadmaster
la source
Non, mais vous pouvez simplement faire if ! command -p sudo; then exit; fice qui aurait les mêmes résultats pour votre exemple.
jordanm
ok, que se passe-t-il si je veux tester le code 127 à la place? (par exemple. [ $? -ne 127 ] && exit $?)
eadmaster
1
@jordanm: Vraiment? Si l'OP a présenté le code / la logique qu'il voulait, et si je le lis correctement, il veut que le script se termine si la sudocommande réussit (c'est-à-dire s'il se sudotermine avec le statut 0). Mais, dans votre code, le script continue de s'exécuter (ne se ferme pas) en cas de sudosuccès
G-Man dit 'Reinstate Monica'
@ G-Man - Je suis presque sûr que les conditions du demandeur sont en fait basées sur le retour commandet pas sudodu tout. C'est ce que signifie que je veux quitter uniquement si la première commande est trouvée (code de sortie! = 127) et est un retour spécifiécommand lorsque la commande qu'il appelle n'est pas trouvée. Je suppose que le problème est que l'invocation sudodans le cadre du test permet en premier lieu d' sudoécraser le retour commandet donc de biaiser le test.
mikeserv

Réponses:

3

$_travaillera en (au moins) interactive dash, bash, zsh, ksh (mais apparemment pas dans une instruction conditionnelle à la demande) et des mkshobus. De ceux-là - à ma connaissance - seulement bashet le zshrempliront également dans un shell scripté. Ce n'est pas un paramètre POSIX - mais il est assez portable pour tout shell moderne et interactif.

Pour une solution plus portable, vous pouvez faire:

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

Ce qui vous permet essentiellement d'étendre la valeur initiale $?dans la queue du script avant même de tester sa valeur à sa tête.

Mais de toute façon, puisque vous semblez tester si la commande sudopeut être trouvée dans la chaîne de -p chemin portable intégrée du shell avec command, je pense que vous pourriez y aller un peu plus directement. En outre, pour être clair, command ne sera pas tester l'emplacement des arguments à sudo- il est donc sudo- et rien n'invoque - ce qui est pertinent à cette valeur de retour.

Et de toute façon, si c'est ce que vous essayez de faire:

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

... fonctionnerait très bien en tant que test sans risque d'erreur dans les sudoexécutions de commandes renvoyant d'une manière qui pourrait fausser les résultats de votre test.

mikeserv
la source
9

Il existe différentes options pour gérer le statut de sortie de manière fiable sans surcharge, en fonction des besoins réels.

Vous pouvez enregistrer l'état de sortie à l'aide d'une variable:

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

Vous pouvez directement vérifier le succès ou l'échec:

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

Ou utilisez une caseconstruction pour différencier le statut de sortie:

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

avec le cas particulier posé dans la question:

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

Toutes ces options ont l'avantage d'être conformes à la norme POSIX.

(Remarque: à titre d'illustration, j'ai utilisé les echocommandes ci-dessus; remplacez-les par des exitinstructions appropriées pour correspondre à la fonction demandée.)

Janis
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
terdon
1
(Je me demande pourquoi le dernier commentaire n'est pas passé au chat.) - Comme déjà expliqué en détail et soutenu par des citations POSIX dans la discussion sur le chat (voir là pour plus de détails); 1.) ("$(get_errnos)")est un ajout artificiel d'une substitution de commande où des comparaisons avec les codes d'erreur réels sont demandées dans la question, 2.) Il y a clairement dans la norme ce qui change $?(et donc ce qui ne le change pas), et 3.) là il n'y a pas d'effets secondaires avec cette construction (sauf si, comme vous l'avez fait, vous les introduisez inutilement). - OTOH, puisque cela vous dérange, l'autre réponse que vous préférez est clairement non standard.
Janis
1
@mikeserv; C'est ennuyeux (et trompeur!) Non seulement parce que vous appliquez des doubles standards ! Si vous appliquiez le même $(get_errnos)code artificiel à d'autres solutions ( ( exit 42 ); test "$(get_errnos)" -ne $? && echo $_), elles ne fonctionnent pas non plus. (Vous avez préféré mettre ma solution standard en faux crédit, pas les autres hack (s) non standard .) - Bien sûr, vous pouvez ajouter du code arbitraire à toutes les réponses et le gâcher. - Et WRT au changement de $?; ce n'est pas parce que vous ne le trouvez pas que ce n'est pas vrai. (J'ai déjà fourni dans nos discussions même les mots-clés à rechercher dans POSIX.)
Janis
1
@mikeserv; Mais c'est, encore une fois, enclin à poursuivre la discussion (infructueuse et sans fin), qui ne devrait pas être placée ici comme @terdon l'a correctement observé.
Janis
1
@mikeserv; J'ai dit tout à fait au début, lorsque vous avez mentionné pour la première fois zsh( mentionné pour la première fois; vous avez également ajouté plus tard dash), que je pense que cela doit être un bogue, étant donné que l' caseétat de sortie et le paramètre de $?semblent bien définis dans POSIX. - Et ici aussi, encore une fois, vous appliquez des doubles standards; l'autre hack, par exemple, n'est ni standard, ni ne fonctionne de manière fiable dans d'autres shells standard (par exemple pas dans ksh). - Mes propositions sont standard et fonctionnent dans bash(principalement utilisé sous Linux) et ksh(le shell prédominant dans les Unix commerciaux).
Janis
7
command -p ...
test 1 -ne $? && exit $_

Use $_, qui se développe jusqu'au dernier argument de la commande précédente.

llua
la source
1
Valable pour cet exemple particulier, mais utilisable uniquement s'il n'y a aucune autre commande entre les références $?et $_. À mon humble avis, il est préférable de s'en tenir à une méthode cohérente qui fonctionne dans d'autres cas (et peut également aider à la lisibilité du code).
Dan Cornilescu
4
Le shell a été spécifiquement nommé deux fois, une fois dans le titre et une fois comme balise. La portabilité n'était pas mentionnée ailleurs dans la question, j'ai donc donné une réponse qui fonctionne dans ledit shell. Quelles autres restrictions étranges vont être inventées?
llua
1
@mikeserv; Au cas où vous l'auriez manqué: j'ai dit: "Il y a eu un tout premier commentaire ici sur POSIX." qui a dû être corrigé. Ni plus ni moins. - Comme vous l'avez longuement débattu et expliqué ici, les trois suggestions de l'autre réponse sont bien définies par POSIX. Pas besoin de répéter votre opinion (OMI fausse) ici, ou de commencer une autre itération du différend.
Janis
1
@mikeserv; Les effets secondaires d'expansion dans les case modèles , le seul endroit qui importerait en théorie (mais pas dans la question donnée), est un cas construit. - Dans tous les cas, votre substitution de commande shell propagerait le résultat de la commande intégrée, généralement d'autres extensions n'affecteront pas l'état de retour de toute façon s'il n'y a pas de commandes impliquées qui peuvent créer des erreurs ( x=${a!b}cas d' esprit , mais non pertinents ici). - Que voulez-vous dire par "commande sans nom de commande" ?
Janis
1
@mikeserv; Il ne peut être encombré que par du code inutile composé de manière ad hoc. Il n'y a absolument aucune zone grise si vous prenez la suggestion sans introduire inutilement un non-sens artificiel. Les exigences étaient absolument claires dans ce cas: 1. exécuter une commande, 2. vérifier le code de sortie, 3. retourner le code de sortie. - Vous saisissez? - Vous avez modifié cette exigence de manière arbitraire pour simplement inventer un argument.
Janis
6

Vous pouvez définir (et utiliser) une fonction shell:

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

alors

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

On peut dire que cela "triche", car il en fait une copie $?dans la check_exit_statusliste des arguments.

Cela peut sembler un peu gênant, et ça l'est. (Cela se produit parfois lorsque vous imposez des contraintes arbitraires aux problèmes.) Cela peut sembler inflexible, mais ce n'est pas le cas. Vous pouvez rendre check_exit_statusplus complexe, en ajoutant des arguments qui lui indiquent quel (s) test (s) faire sur la valeur d'état de sortie.

Scott
la source
0

Pour répondre à votre question directe, non, il n'est pas possible de ne pas la $?modifier. Et c'est l'un de ces cas où je soupçonne que vous vous concentrez sur le mauvais problème. Une variable temporaire est le moyen standard et préféré d'obtenir l'effet que vous recherchez. C'est tellement standard que je suggérerais d'abandonner (ou de repenser) quelle que soit la raison que vous pensez avoir pour ne pas vouloir en utiliser un; Je doute fortement que cela vaille la complexité supplémentaire ajoutée par toute autre méthode.

Si vous demandez simplement par simple curiosité, la réponse est non.

David Z
la source
@mikeserv Je ne suis pas sûr de comprendre - parlez-vous d'assigner manuellement $?ou quelque chose comme ça?
David Z
0

Voici mon extrait de code pour conserver un état de sortie précédent sans quitter le script / shell actuel

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