Come mantenere l'ultimo stato di uscita dopo il test


8

È possibile mantenere inalterato l'ultimo comando exit status ( $?) dopo un test?

Ad esempio, vorrei fare:

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

L'ultimo exit $?dovrebbe restituire lo stato di uscita sudo, ma invece restituisce sempre 0(il codice di uscita del test).

È possibile farlo senza una variabile temporanea?

Un altro esempio per chiarire ulteriormente:

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

In questo caso voglio uscire solo se viene trovato il primo comando (codice di uscita! = 127). E voglio uscire con il spd-saycodice di uscita effettivo (potrebbe non essere 0).

EDIT: ho dimenticato di dire che preferisco una soluzione di reclamo POSIX per una migliore portabilità.

Uso questo costrutto negli script in cui desidero fornire alternative per lo stesso comando. Ad esempio, vedi il mio script crc32 .

Il problema con le variabili temporanee è che potrebbero oscurare altre variabili ed evitare che sia necessario utilizzare nomi lunghi, il che non è utile per la leggibilità del codice.


No, ma puoi semplicemente fare if ! command -p sudo; then exit; fiche avrebbe gli stessi risultati per il tuo esempio.
Giordania,

ok, e se invece volessi provare il codice 127? (es. [ $? -ne 127 ] && exit $?)
eadmaster il

1
@jordanm: Davvero? Se l'OP ha presentato il codice / la logica che intendeva fare, e se lo sto leggendo correttamente, vuole che lo script esca se il sudocomando ha esito positivo (ovvero, se sudoesce con lo stato 0). Ma, nel tuo codice, lo script continua a funzionare (non esce) se sudoriesce
G-Man dice 'Reinstate Monica' il

@ G-Man - Sono abbastanza sicuro che le condizioni del richiedente siano effettivamente basate sul ritorno di commande sudoper niente. Questo è ciò che si intende per voglio uscire solo se viene trovato il primo comando (codice di uscita! = 127) ed è un ritorno specificato percommand quando il comando che invoca non viene trovato. Immagino che il problema sia che l'invocazione sudocome parte del test consente di sudoschiacciare il ritorno di commandin primo luogo e quindi distorcere il test.
Mikeserv,

Risposte:


3

$_lavorerà in (almeno) interattivo dash, bash, zsh, ksh (anche se a quanto pare non in un'istruzione condizionale come richiesto) e mkshconchiglie. Di quelli - per quanto ne so - solo bashe zshlo popoleranno anche in un guscio con script. Non è un parametro POSIX, ma è abbastanza portabile su qualsiasi shell moderna e interattiva.

Per una soluzione più portatile puoi fare:

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

Il che sostanzialmente ti consente di espandere il valore iniziale $?nella coda dello script prima ancora di testarne il valore alla testa.

Ma comunque, dato che sembra che tu stia testando se il comando sudopuò essere trovato nella stringa del -p percorso portatile incorporato nella shell command, penso che potresti farcela più direttamente. Inoltre, solo per essere chiari, command non verificherà la posizione di alcun argomento per sudo- quindi è solo sudo- e nulla invoca - che è rilevante per quel valore di ritorno.

E comunque, se è quello che stai cercando di fare:

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

... funzionerebbe perfettamente come test senza alcuna possibilità di errori nell'esecuzione del comando che sudoritorni in modo tale da distorcere i risultati del test.


9

Esistono varie opzioni per gestire lo stato di uscita in modo affidabile senza spese generali, a seconda dei requisiti effettivi.

È possibile salvare lo stato di uscita utilizzando una variabile:

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

Puoi verificare direttamente il successo o il fallimento:

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

Oppure usa un casecostrutto per differenziare lo stato di uscita:

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

con il caso speciale posto nella domanda:

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

Tutte queste opzioni hanno il vantaggio di essere conformi allo standard POSIX.

(Nota: per l'illustrazione ho usato i echocomandi sopra; sostituisci con le exitistruzioni appropriate per abbinare la funzione richiesta.)


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
terdon

1
(Mi chiedo perché l'ultimo commento non sia passato alla chat.) - Come già spiegato in modo thorogly e supportato dalle citazioni POSIX nella discussione della chat (vedere qui per i dettagli); 1.) ("$(get_errnos)")è un'aggiunta artificiale di una sostituzione di comando in cui vengono richiesti confronti con codici di errore effettivi nella domanda, 2.) È chiaro nello standard cosa cambia $?(e quindi cosa non lo cambia), e 3.) lì non sono effetti collaterali con quel costrutto (a meno che, come hai fatto tu, non li presenti inutilmente). - OTOH, poiché ti dispiace, l'altra risposta che preferisci è chiaramente non standard.
Janis,

1
@mikeserv; È fastidioso (e fuorviante!) Non solo perché si applicano doppi standard ! Se applichi lo stesso $(get_errnos)codice artificiale a qualsiasi altra soluzione ( ( exit 42 ); test "$(get_errnos)" -ne $? && echo $_), anche questi non funzionano. (Hai preferito portare la mia soluzione standard in errore, non gli altri hack non standard .) - Naturalmente puoi aggiungere codice arbitrario a qualsiasi risposta e rovinarlo. - E WRT al cambio di $?; solo perché non riesci a trovarlo non significa che non sia vero. (Nelle nostre discussioni ho già fornito anche le parole chiave da cercare in POSIX.)
Janis,

1
@mikeserv; Ma questo è, ancora una volta, incline a continuare la discussione (infruttuosa e senza fine), che non dovrebbe essere messa qui come ha osservato correttamente @terdon.
Janis,

1
@mikeserv; All'inizio ho detto che hai menzionato per la prima volta zsh( menzionato per la prima volta da solo; in seguito hai anche aggiunto dash) che penso che debba essere un bug lì, dato che lo casestato di uscita e l'impostazione di $?sembrano ben definiti in POSIX. - E anche qui, di nuovo, si applicano doppi standard; l'altro hack, ad esempio, non è né standard, né funziona in modo affidabile in altre shell standard (ad es. non in ksh). - Le mie proposte sono standard e funzionano in bash(principalmente utilizzate su Linux) e ksh(la shell predominante negli Unix commerciali).
Janis,

7
command -p ...
test 1 -ne $? && exit $_

Usa $_, che si espande fino all'ultimo argomento del comando precedente.


1
Valido per questo esempio particolare, ma utilizzabile solo se non sono presenti altri comandi tra i riferimenti $?e $_. IMHO è meglio attenersi a un metodo coerente che funziona in altri casi (e può anche aiutare con la leggibilità del codice).
Dan Cornilescu,

4
La shell è stata specificamente chiamata due volte, una volta nel titolo e una volta come tag. Né la portabilità è stata menzionata in nessuna parte della domanda, quindi ho dato una risposta che funziona in detta shell. Quali altre strane restrizioni verranno inventate?
llua,

1
@mikeserv; Nel caso in cui tu lo abbia perso: ho detto: "C'era un primo commento qui su POSIX". che doveva essere corretto. Ne più ne meno. - Come discusso approfonditamente con te e spiegato qui, tutti e tre i suggerimenti nell'altra risposta sono ben definiti da POSIX. Non è necessario ripetere qui la tua opinione (errata IMO) o iniziare un'altra iterazione della controversia.
Janis,

1
@mikeserv; Gli effetti collaterali dell'espansione nei case modelli , l'unico posto che avrebbe importanza in teoria (ma non nella domanda data), è un caso costruito. - In ogni caso, la sostituzione del comando shell propagherà il risultato del comando incorporato, di solito altre espansioni non influiranno comunque sullo stato di ritorno se non sono coinvolti comandi in grado di creare errori ( x=${a!b}casi mentali , ma qui irrilevanti). - Cosa intendi con "comando senza nome comando" ?
Janis,

1
@mikeserv; Può essere bloccato solo da un codice superfluo creato ad hoc. Non c'è assolutamente alcuna area grigia se si accetta il suggerimento senza introdurre inutilmente sciocchezze artificiali. In questo caso i requisiti erano assolutamente chiari: 1. eseguire un comando, 2. controllare il codice di uscita, 3. restituire il codice di uscita. - Lo capisci? - Hai modificato arbitrariamente tale requisito per inventare una discussione.
Janis,

6

Puoi definire (e usare) una funzione shell:

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

Poi

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

Probabilmente, questo è "barare", dal momento che ne fa una copia $?nell'elenco degli check_exit_statusargomenti.

Questo può sembrare un po 'imbarazzante, e lo è. (Questo a volte succede quando imponi vincoli arbitrari ai problemi.) Ciò può sembrare poco flessibile, ma non lo è. È possibile rendere check_exit_statuspiù complesso, aggiungendo argomenti che gli dicono cosa devono fare i test sul valore dello stato di uscita.


0

Per rispondere alla tua domanda diretta, no, non è possibile mantenere $? inalterati. E questo è uno di quei casi in cui sospetto che ti stai concentrando sul problema sbagliato. Una variabile temporanea è il modo standard e preferito per ottenere l'effetto che stai cercando. È così standard che suggerirei di abbandonare (o ripensare) qualunque motivo tu pensi di avere per non volerne usare uno; Dubito fortemente che valga la complessità aggiuntiva aggiunta da qualsiasi altro metodo.

Se stai solo chiedendo per semplice curiosità, la risposta è no.


@mikeserv Non sono sicuro di aver capito - stai parlando di assegnare manualmente $?o qualcosa del genere?
David Z,

0

Ecco il mio frammento di codice per conservare uno stato di uscita precedente senza uscire dallo script / shell corrente

EXIT_STATUS=1
if [[ $EXIT_STATUS == 0 ]]
then
  echo yes
else
  (exit $EXIT_STATUS)
fi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.