Vous utilisez $? dans une instruction if

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

J'essaie d'écrire une fonction comme celle ci-dessus et de la placer dans mon fichier .bashrc. Après avoir source le fichier et exécuté, j'obtiens:

Temps total: 51 secondes
-bash: [1: commande introuvable
OK!

Quelqu'un peut-il m'aider à comprendre ce que j'ai fait de mal?

Amir Afghani
la source
4
Tester si $?est égal à 0 avec une ifinstruction est inutile, ifattend une commande et si cette commande revient 0, il exécute le code dans le bloc. ainsi if true; then echo hello; fifera écho bonjour depuis le trueretour de la commande 0.
llua
1
@llua Ce n'est pas inutile. $?contient le statut du dernier pipeline , qui n'est pas la commande test( [) dans l' ifinstruction. L'exemple teste la some commandréussite. Vous pouvez faire de même avec &&et ||, mais cela peut faire de longues lignes illisibles par rapport à if [ $? -eq 0 ]. Le même argument vaut pourif some command
bonsaiviking
@bonsaiviking Je suis bien conscient de ce qui se $?développe, je souligne qu'il n'y a aucun intérêt à testêtre utilisé; car if some commandfait la même chose avec une seule instruction par rapport à deux instructions distinctes. si la commande est déjà longue, l'ajout de trois caractères supplémentaires ne rendrait pas «illisible» terriblement plus «illisible».
llua

Réponses:

33

Ajoutez un espace après le [, et un autre avant ]:

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

[est une commande du shell, il est une commande comme echo, read, expr... il a besoin d' un espace après, et nécessite une correspondance ].

L' écriture [ $? -ne 0 ]est en fait invoquaient [et lui donner 4 paramètres: $?, -ne, 0et ].

Remarque: le fait que vous obteniez une erreur en disant [1: command not foundque cela $?avait la valeur de 1.

aularon
la source
1
Je viens de vérifier que votre réponse est correcte sur ma machine virtuelle Linux.
samiam
[est un lien vers testaussi bien
Ricky faisceau
2
@RickyBeam dans la plupart des shells, [est un shell intégré, et le /usr/bin/[est rarement utilisé.
Patrick
20

Ou vous pouvez sauter $?complètement. Si votre commande est cmd, les éléments suivants devraient fonctionner:

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

Il est recommandé d'affecter la valeur de retour à une variable avant de l'utiliser

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

Il vous permet de réutiliser la valeur de retour. par exemple dans une instruction if ... elif ... else ....

Abdul
la source
-1: Vous avez oublié l'espace après [(Sans l'espace, cela devient une erreur de syntaxe dans bash) Annulera si vous modifiez et corrigez votre erreur.
samiam
Oui, tu as raison. Je l'ai corrigé.
Abdul
@samiam que le dernier commentaire vous a été
adressé
4
:) Pourriez-vous développer un peu votre réponse. Pourquoi est-ce une bonne pratique? Quels types d'erreurs pourraient être évités?
terdon
1
@terdon c'est une mauvaise pratique d'utiliser $?directement parce que ça casse si jamais, lors de l'édition du script plus tard, mettez une ligne entre la commande et la $?vérification.
samiam
3

La seule raison pour laquelle vous souhaitez utiliser $?comme arguments d'une [commande (que cette [commande soit exécutée dans la partie condition d'une ifinstruction ou non) est lorsque vous souhaitez discriminer sur un état de retour spécifique, comme:

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

La syntaxe pour tous ceux if, while, until... des déclarations est

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

Qui s'exécute en cmd-list2cas de cmd-list1succès ou cmd-list3non.

La [ "$?" -eq 0 ]commande est un no-op. Il est défini $?sur 0 si $?est $?égal à 0 et sur non nul s'il était différent de zéro.

Si vous voulez exécuter quelque chose en cas d' cmdéchec, c'est:

if ! cmd
then ...
fi

Généralement, vous n'avez pas besoin de bricoler et $?encore moins de savoir quelle valeur signifie trueou false. Les seuls cas sont comme je l'ai dit ci-dessus si vous devez discriminer sur une valeur spécifique, ou si vous devez l'enregistrer pour plus tard (par exemple pour la retourner comme valeur de retour d'une fonction) comme:

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

Souvenez-vous également que laisser une variable sans guillemets est l'opérateur split + glob. Cela n'a aucun sens d'invoquer cet opérateur ici, donc ça devrait être:

[ "$?" -ne 0 ]

pas [ $? -ne 0 ], et encore moins [$? -ne 0 ](qui n'appellerait la [commande que s'il $IFScontenait le premier caractère de $?).

Notez également que la manière Bourne de définir une fonction est de rester function-name()devant une commande. C'est le cas dans tous les shell Bourne like sauf bashet yash(et les versions récentes de posh) qui n'autorisent qu'une commande composée (les commandes composées étant {...}ou (...)ou des choses comme for...done, if...fi...

function foo { ... }est la kshsyntaxe de définition de fonction. Il n'y a aucune raison pour que vous souhaitiez l'utiliser ici.

Votre code peut être écrit de manière portable (POSIX):

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

Notez également que cdsans -Pa une signification très spéciale (gère les chemins qui contiennent des ..composants différemment de toute autre commande), il est donc préférable de l'inclure dans les scripts pour éviter les confusions.

(cette fonction renvoie en falsecas d' cdéchec, mais pas en cas d' <some command>échec).

Stéphane Chazelas
la source
1

Je crois que la commande ci-dessous fera tout ce que vous voulez en une seule ligne.

(( verify = $?!=0?'Nope!':'OK!' ))
Jeight
la source