¿Cómo salir de un script de shell si una parte falla?

51

¿Cómo puedo escribir un script de shell que salga, si una parte falla? Por ejemplo, si falla el siguiente fragmento de código, la secuencia de comandos debería salir.

n=0
until [ $n -ge 5 ]
do
  gksu *command* && break
  n=$[$n+1]
  sleep 3
Weylyn
fuente

Respuestas:

88

Un enfoque sería agregar set -eal comienzo de su secuencia de comandos. Eso significa (de help set):

  -e  Exit immediately if a command exits with a non-zero status.

Entonces, si alguno de sus comandos falla, el script se cerrará.

Alternativamente, puede agregar exitdeclaraciones explícitas en los posibles puntos de falla:

command || exit 1
terdon
fuente
55
Yo (y el wiki bash ) preferiría que la gente piense en el manejo adecuado de errores en lugar de usar la función (roto por diseño IMO) set -e. Sin embargo, realmente no se aplica aquí. El OP quiere salir del script después de 5 intentos fallidos de ejecutar el comando.
Stéphane Chazelas
1
@ StéphaneChazelas No discutiré contigo si está roto, estoy seguro de que tienes razón. Sin embargo, el OP preguntó "¿Cómo puedo escribir un script de shell que salga, si una parte falla?", ¿Qué te hace pensar que se trata de salir después de 5 fallas?
terdon
porque no se me ocurre otra forma de interpretar la pregunta.
Stéphane Chazelas
1
@ StéphaneChazelas, puede que tengas razón. Lo interpreté literalmente: ¿cómo puede salir todo el script si alguna parte falla? Y set -ees la única forma en que sé hacer eso.
terdon
En ese fragmento de secuencia de comandos, los comandos que podrían activarse set -eson sleep(al breakser una construcción especial, la secuencia de comandos se cerrará al fallar en la mayoría de los shells, los comandos dentro ifo a la izquierda &&no se verán afectados set -e, n=...podrían fallar si nes de solo lectura, pero luego eso también saldría del guión set -e), por lo que la interpretación suena bastante improbable. Estoy de acuerdo en que la pregunta está mal redactada.
Stéphane Chazelas
17

Puede salir de un script en cualquier lugar utilizando la palabra clave exit. También puede especificar un código de salida con el fin de indicar a los demás programas que o cómo suspendido el guión, por ejemplo, exit 1o exit 2etc (Por convención, el código de salida 0 es para el éxito y el fracaso mayor cosa que no sea 0 significa, sin embargo, también por convención, salida los códigos superiores a 127 están reservados para la terminación anormal (por ejemplo, por una señal)).

La construcción genérica para salir en caso de falla es

if [ failure condition ]; then
    exit n
fi

Con adecuado failure conditiony n. Pero en escenarios específicos puede proceder de manera diferente. Ahora, para su caso, interpreto su pregunta de que si alguna de las cinco invocaciones de gksuerror falla, entonces quiere decir salir. Una forma es usar una función como esta

function try_command {
    for i in 1 2 3 4 5 ; do
        if gksu command ; then
            return 0
        fi
    fi
    exit 1
}

y luego invoque el bucle try_command.

Hay (más) formas avanzadas o sofisticadas de cómo abordar su pregunta. Sin embargo, la solución anterior es más accesible para los principiantes que, por ejemplo, la solución de Stephane.

contramodo
fuente
10
attempt=0
until gksu command; do
  attempt=$((attempt + 1))
  if [ "$attempt" -gt 5 ]; then
    exit 1
  fi
done

exitsale del script a menos que se llame en una subshell. Si esa parte de la secuencia de comandos se encuentra en un subnivel, por ejemplo, porque está dentro (...)o $(...)o parte de una línea de tubería, entonces sólo saldrá de que subnivel .

En ese caso, si desea que el script salga además de la subshell, deberá llamar exita esa subshell para que salga.

Por ejemplo, aquí con 2 niveles anidados de subcapas:

(
  life=hard
  output=$(
    echo blah
    [ "$life" = easy ] || exit 1 # exit subshell
    echo blih not run
  ) || exit # if the subshell exits with a non-zero exit status,
            # exit as well with the same exit status

  echo not run either
) || exit # if the subshell exits with a non-zero exit status,
          # exit as well with the same exit status

Puede volverse más complicado si la subshell es parte de una tubería. bashtiene una $PIPESTATUSmatriz especial , similar a zshla $pipestatusque puede ayudarte aquí:

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi
Stéphane Chazelas
fuente
3

Trap realizará una acción al recibir una señal.

trap "echo EXIT;  exit" 0
trap "echo HUP;   exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT;  exit" 3
trap "echo ERR;   exit" ERR
n=0
until [ $n -ge 5 ]
do
  n=$[$n+1]
  echo $n
  sleep 3
done

Ejecuta esto y deja que salga normalmente. Atrapa la señal 0.

EXIT

Ejecútelo de nuevo e interrumpa con ^ C. Atrapa tanto la señal 2 como la señal 0.

CTL-C
EXIT

Un estado de salida distinto de cero quedará atrapado en ERR

ERR
EXIT
RobP
fuente