¿Cuál es el equivalente a && al escribir un script bash?

31

Pido disculpas de antemano si esta es una pregunta duplicada. Hice un esfuerzo para buscar / verificar antes de preguntar aquí.

Me siento cómodo escribiendo frases sencillas como esta:

foocommand && foocommand2 && foocommand3

La idea es que solo quiero que se ejecuten comandos posteriores si el anterior fue "exitoso".

Estoy escribiendo un guión algo largo y esta frase no es factible porque parece un gran bloque de código confuso para todos los demás.

Quiero espaciar los comandos y escribir comentarios entre ellos en el script. ¿Cómo puedo hacer esto y aún tener el equivalente de && allí?

Mike B
fuente
44
Esta repetición de los osos: &&no significa que el comando posterior se ejecutará si el anterior se ha realizado correctamente. Significa que el comando se ejecutará si el resultado colectivo de todos los comandos anteriores en la lista de comandos es exitoso. Puede saber esto, pero los futuros lectores pueden malinterpretar.
kojiro
1
También como una nota, este comportamiento que está describiendo proviene ||y &&(en oposición a |o &) se llama cortocircuito. El comportamiento utilizado con estos últimos operadores se llama evaluación entusiasta.
AndyPerfect
77
@kojiro: No veo la distinción. a && b && csolo se ejecutará bsi atiene éxito, por lo que "solo se ejecuta csi btiene éxito" y "solo se ejecuta csi ay bambos tienen éxito" son declaraciones equivalentes: bno puede tener éxito a menos que tenga aéxito.
ruakh
1
@ruakh a || b && ces más ilustrativo.
kojiro
8
@ruakh no, true || false && echo hisaldrá hi. La especificación dice: Los operadores "&&" y "||" tendrá la misma precedencia y se evaluará con asociatividad izquierda.
kojiro

Respuestas:

23

Puedes hacerlo así:

#!/bin/sh
ls -lh &&
    # This is a comment
    echo 'Wicked, it works!'

Espero haber entendido lo que preguntaste correctamente.

schaiba
fuente
Gracias. Esto es lo más cercano a lo que esperaba hacer. Y hace que sea muy fácil volver a convertirlo en un solo revestimiento si es necesario.
Mike B
Mike, dado que te preocupa la limpieza del código, una convención común es sangrar las secciones 2-N de la cadena debajo de la primera.
jblaine
@jblaine No estoy seguro de entender lo que quieres decir. ¿Puedes por favor elaborar?
Mike B
Mike, Stackexchange no me deja formatear mi respuesta correctamente, así que aquí está todo lo que quise decir: sangría
jblaine
@jblaine Buen punto, editado para sangrar las líneas.
Volker Siegel
38

Puede cambiar la línea shebang a

#!/bin/bash -e

Después de cualquier error, el script se detendrá.

choroba
fuente
1
Interesante. Lo tendré en mente. Gracias.
Mike B
me ahorra escribir set -e cuando lo necesito
Silverrocker
@Silverrocker También es POSIX (excepto por la bashparte, por supuesto). El shell POSIX toma un montón de opciones que son aplicables a set. ¿O viceversa? setes una forma de configurar las opciones de línea de comandos de shell dentro del script
Kaz
77
Desafortunadamente, hay una serie de utilidades estándar que no siguen las convenciones de estado de salida habituales, por lo que -epueden comportarse mal. Por ejemplo, probablemente no desee que su script termine cada vez que diffdos archivos no idénticos. Puede, por supuesto, solucionar esto agregando || true(o tal vez || [ $? = 1 ]) a tales comandos, pero el OP menciona a "todos los demás" que tendrán que leer este script, y dijo que "todos los demás" probablemente no estén acostumbrados a tener que hacer frente -e.
ruakh
44
Solía ​​poner el -eshe-bang, hasta que un administrador de mi compañía siguió lanzando mis scripts bash myscript.sh, por lo que ignoró el -e. Ahora todos mis guiones tienen un set -een la segunda línea.
Carlos Campderrós
19

Si no te gusta la set -eidea, tal vez puedas invertir la lógica.

foocommand || exit 1
foocommand2 || exit 2
foocommand3 || exit 3

Más útil, reemplace exitcon algo para imprimir un mensaje de error útil, luego salga. Dentro de una función, por supuesto, quieres en returnlugar de exit.

tripleee
fuente
11
O foocommand || exitpara salir con foocommandel estado de salida si no es cero.
Stéphane Chazelas
¿Como funciona esto? ¿Es como en C: si el argumento izquierdo se evalúa como VERDADERO, no se verifican más argumentos?
Vorac
Si, eso es correcto. Es un idioma común en Lisp (y lenguajes funcionales en general, supongo) y en Perl.
tripleee
9

Puedes usar if else fibloques en su lugar.

if foocommand; then

  # some comments

  if foocommand2; then

    # more comments

    foocommand3
  fi
fi

Es un poco más legible.

Alternativamente, puede usar \para dividir su gran liner en varias líneas

foocommand && \
  # some comment
  foocommand2 && \
    # more comment
    foocommand3

Pero, por supuesto, esto puede ser confuso para el ojo inexperto.

Martín Canaval
fuente
44
No creo que \sea ​​necesario allí.
Samuel Edwin Ward
Tienes razón. Fuerza de la costumbre.
Martín Canaval
5

Podría considerar el uso de ifdeclaraciones anidadas . Otra opción es usar rizados para agrupar como{ true && echo hi; } || echo huh

Actualización 1:

Aquí hay un ejemplo sin saltos / comentarios:

{ { false && echo "inner true"; } && { echo "inner true" && true; } || { echo "inner false" && false; } || echo "outter false"; }
livingstaccato
fuente
@ Stephane Gracias por agregar punto y coma. He estado usando mi teléfono para responder, así que no he vuelto a verificar mis respuestas. :)
livingstaccato
Idea interesante. Sí, había pensado en declaraciones if anidadas, pero si estoy encadenando muchas cosas juntas, podría ser complicado.
Mike B
4

Divide cada secuencia de comandos en funciones:

big_block_1() {
  # ...
}
big_block_2() {
  # ...
}
big_block_3() {
  # ...
}

big_block_1 && big_block_2 && big_block_3
Fuad Saud
fuente
0

Siempre me gusta escribir una función de "falla" que me permite especificar lo que iba a hacer cuando ocurrió la falla y luego usar el ||patrón. Como el siguiente:

function fail() {
  echo "Failure: ${@}"
  exit 1
}

foocommand || fail "couldn't foo"
Dave
fuente