Usando $? en una declaración if

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

Estoy tratando de escribir una función como la de arriba y colocarla en mi archivo .bashrc. Después de obtener el archivo y ejecutarlo, obtengo:

Tiempo total: 51 segundos -
golpe: [1: comando no encontrado
¡OK!

¿Alguien puede ayudarme a entender lo que hice mal?

Amir Afghani
fuente
44
Probar si $?es igual a 0 con una ifinstrucción no tiene sentido, ifespera un comando y si dicho comando regresa 0, ejecuta el código en el bloque. entonces se if true; then echo hello; fihará eco desde que el comando trueregresó 0.
llua
1
@llua No tiene sentido. $?mantiene el estado de la última tubería , que no es el comando test( [) en la ifdeclaración. El ejemplo es probar si some commandfue exitoso. Puede hacer lo mismo con &&y ||, pero puede hacer líneas largas e ilegibles en comparación con if [ $? -eq 0 ]. El mismo argumento es if some command
válido
@bonsaiviking Soy muy consciente de lo que se $?expande, estoy señalando que no tiene sentido testser utilizado; ya que if some commandhace lo mismo con una declaración versus tener dos declaraciones separadas. si el comando ya es largo, agregar tres caracteres más no haría que lo 'ilegible' sea terriblemente más 'ilegible'.
llua

Respuestas:

33

Agregue un espacio después del [, y otro antes ]:

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

[es una orden interna del shell, es un comando al igual que echo, read, expr... necesita un espacio después de ella, y requiere una coincidencia ].

La escritura [ $? -ne 0 ]es en realidad invocando [y dándole 4 parámetros: $?, -ne, 0, y ].

Nota: el hecho de que recibas un mensaje de error [1: command not foundsignifica que $?estaba teniendo el valor de 1.

aularon
fuente
1
Acabo de verificar que su respuesta es correcta en mi máquina virtual Linux.
samiam
[es un enlace testtambién
Ricky Beam
2
@RickyBeam en la mayoría de las conchas, [es una concha incorporada, y /usr/bin/[rara vez se usa.
Patrick
20

O podrías saltarte por $?completo. Si su comando es cmd, lo siguiente debería funcionar:

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

Es una buena práctica asignar el valor de retorno a una variable antes de usarla.

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

Le permite reutilizar el valor de retorno. por ejemplo, en una declaración if ... elif ... else ...

Abdul
fuente
-1: Olvidó el espacio después [(sin el espacio, se convierte en un error de sintaxis en bash) Se deshará si edita y corrige su error.
samiam
Sí, tienes razón. Lo arreglé.
Abdul
@samiam ese último comentario fue dirigido a usted
terdon
44
:) ¿Podría ampliar su respuesta un poco? ¿Por qué es una buena práctica? ¿Qué tipo de errores podrían evitarse?
terdon
1
@terdon es una mala práctica usarlo $?directamente porque eso se romperá si alguna vez, al editar el script más adelante, coloca una línea entre el comando y el $?cheque.
samiam
3

La única razón por la que desea usar $?como argumentos para un [comando (si ese [comando se ejecuta en la condición de una ifdeclaración o no) es cuando desea discriminar en un estado de retorno específico, como:

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

La sintaxis para todos aquellos if, while, until... declaraciones es

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

Que se ejecuta cmd-list2si cmd-list1tiene éxito o cmd-list3no.

El [ "$?" -eq 0 ]comando es un no-op. Se establece $?en 0 si $?es 0 y $?en no cero si no era cero.

Si quieres ejecutar algo si cmdfalla, es:

if ! cmd
then ...
fi

En general, no es necesario jugar con $?mucho menos saber qué valor significa trueo false. Los únicos casos son como dije anteriormente si necesita discriminar un valor específico, o si necesita guardarlo para más adelante (por ejemplo, para devolverlo como el valor de retorno de una función) como:

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

También recuerde que dejar una variable sin comillas es el operador split + glob. No tiene sentido invocar a ese operador aquí, por lo que debería ser:

[ "$?" -ne 0 ]

no [ $? -ne 0 ], y mucho menos [$? -ne 0 ](que solo invocaría el [comando si $IFScontiene el primer carácter de $?).

También tenga en cuenta que la forma Bourne de definir una función es quedarse function-name()delante de un comando. Ese es el caso en cada Bourne shell como excepción bashy yash(y versiones recientes de posh), que sólo permite un comando compuesto (comandos compuesto que está siendo {...}o (...)o cosas como for...done, if...fi...

function foo { ... }es la kshsintaxis de definición de función. No hay razón por la que quieras usarlo aquí.

Su código puede escribirse de forma portátil (POSIXY):

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

También tenga en cuenta que cdsin -Ptiene un significado muy especial (maneja rutas que contienen ..componentes de manera diferente a cualquier otro comando), por lo que es mejor incluirlo en los scripts para evitar confusiones.

(esa función devuelve falsesi cdfalla, pero no si <some command>falla).

Stéphane Chazelas
fuente
1

Creo que el siguiente comando hará todo lo que quieras en una línea.

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