Usando $? in un'istruzione if


12
function foo {
   (cd $FOOBAR;
   <some command>
   if [$? -ne 0]
   then
      echo "Nope!"
   else
      echo "OK!"
   fi
   )
}

Sto cercando di scrivere una funzione come quella sopra e inserirla nel mio file .bashrc. Dopo aver creato il file ed eseguito, ottengo:

Tempo totale: 51 secondi
-bash: [1: comando non trovato
OK!

Qualcuno può aiutarmi a capire cosa ho fatto di sbagliato?


4
Testare se $?è uguale a 0 con ifun'istruzione è inutile, si ifaspetta un comando e se detto comando ritorna 0, esegue il codice nel blocco. così if true; then echo hello; fifarà eco ciao da quando il comando è truetornato 0.
llua,

1
@llua Non è inutile. $?contiene lo stato dell'ultima pipeline , che non è il comando test( [) ifnell'istruzione. L'esempio sta verificando se ha some commandavuto successo. Puoi fare lo stesso con &&e ||, ma può fare linee lunghe e illeggibili rispetto a if [ $? -eq 0 ]. Lo stesso argomento vale perif some command
bonsaiviking,

@bonsaiviking Sono ben consapevole di ciò che si $?espande, sto sottolineando che non ha senso testusare; poiché if some commandfa la stessa cosa con un'affermazione rispetto ad avere due dichiarazioni separate. se il comando è già lungo, l'aggiunta di altri tre caratteri non renderebbe il 'illeggibile' terribilmente più 'illeggibile'.
llua,

Risposte:


33

Aggiungi uno spazio dopo il [e un altro prima ]:

function foo {
   (cd $FOOBAR;
   <some command>
   if [ $? -ne 0 ]
   then
      echo "Nope!"
   else
      echo "OK!"
   fi
   )
}

[è un comando shell, è un comando come echo, read, expr... necessita di uno spazio dopo di esso, e richiede una corrispondenza ].

La scrittura [ $? -ne 0 ]è in realtà invoca [e dandogli 4 parametri: $?, -ne, 0, e ].

Nota: il fatto che stai ricevendo un errore dicendo [1: command not foundsignifica che $?stava avendo il valore di 1.


1
Ho appena verificato che la tua risposta sia corretta sulla mia macchina virtuale Linux.
Samiam,

[è un collegamento a testcosì
Ricky fascio

2
@RickyBeam nella maggior parte delle shell, [è incorporato nella shell e /usr/bin/[viene usato raramente.
Patrick,

20

Oppure potresti saltare del $?tutto. Se il tuo comando è cmd, dovrebbe funzionare quanto segue:

function foo {
   (cd $FOOBAR;
   if cmd
   then
      echo "OK!"
   else
      echo "Nope!"
   fi
   )
}

6

È buona norma assegnare il valore restituito a una variabile prima di usarlo

retval="$?"
if [ $retval -ne 0 ]

Ti consente di riutilizzare il valore restituito. ad es. in un'istruzione if ... elif ... else ...


-1: Hai dimenticato lo spazio dopo [(Senza lo spazio, diventa un errore di sintassi in bash) Annulla se modifichi e correggi l'errore.
Samiam,

Sì, hai ragione. L'ho riparato.
Abdul

@samiam quell'ultimo commento è stato diretto a te
terdon

4
:) Potresti espandere leggermente la tua risposta. Perché è una buona pratica? Che tipo di errori si possono evitare?
terdon

1
@terdon è una cattiva pratica da usare $?direttamente perché ciò si interromperà se mai, quando si modifica lo script in seguito, si inserisce una linea tra il comando e il $?controllo.
Samiam,

3

L'unico motivo per cui si desidera utilizzare $?come argomenti un [comando (indipendentemente dal fatto che quel [comando venga eseguito nella parte condizione di ifun'istruzione o meno) è quando si desidera discriminare uno stato di restituzione specifico, come:

until
  cmd
  [ "$?" -gt 1 ]
do
  something
done

La sintassi per tutti coloro if, while, until... affermazioni è

if cmd-list1
then cmd-list2
else cmd-list3
fi

Che funziona cmd-list2se ha cmd-list1successo o cmd-list3meno.

Il [ "$?" -eq 0 ]comando è no-op. Imposta $?su 0 se $?è 0 e $?su zero se era diverso da zero.

Se vuoi eseguire qualcosa se cmdfallito, è:

if ! cmd
then ...
fi

In generale, non è necessario armeggiare con per $?non parlare del valore trueo false. Gli unici casi sono come ho detto sopra se è necessario discriminare un valore specifico o se è necessario salvarlo per dopo (ad esempio per restituirlo come valore di ritorno di una funzione) come:

f() {
  cmd; ret=$?
  some cleanup
  return "$ret"
}

Ricorda inoltre che lasciare una variabile non quotata è l'operatore split + glob. Non ha senso invocare questo operatore qui, quindi dovrebbe essere:

[ "$?" -ne 0 ]

no [ $? -ne 0 ], figuriamoci [$? -ne 0 ](che invocherebbe il [comando solo se $IFSdovesse contenere il primo carattere di $?).

Si noti inoltre che il modo Bourne per definire una funzione è quello di rimanere function-name()davanti a un comando. Questo è il caso di ogni shell come Bourne tranne bashe yash(e versioni recenti di posh) che consentono solo un comando composto (comandi composti essendo {...}o (...)o cose del genere for...done, if...fi...

function foo { ... }è la kshsintassi della definizione della funzione. Non c'è motivo per cui vorresti usarlo qui.

Il tuo codice può essere scritto in modo portabile (POSIX):

foo() (
  cd -P -- "$FOOBAR" || return # what if the cd failed!
  if
    <some command>
  then
    echo 'OK!'
  else
    echo 'Nope!'
  fi
)

Si noti inoltre che cdsenza -Pha un significato molto speciale (gestisce percorsi che contengono ..componenti in modo diverso da qualsiasi altro comando), quindi è meglio includerlo negli script per evitare confusioni.

(quella funzione ritorna falsese cdfallisce, ma non se <some command>fallisce).


1

Credo che il comando seguente farà tutto ciò che vuoi in una riga.

(( verify = $?!=0?'Nope!':'OK!' ))
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.