¿Qué hace 'set -e' y por qué podría considerarse peligroso?


147

Esta pregunta apareció en un cuestionario previo a la entrevista y me está volviendo loco. ¿Alguien puede responder esto y tranquilizarme? La prueba no tiene referencia a un shell en particular, pero la descripción del trabajo es para un sa de Unix. de nuevo la pregunta es simplemente ...

¿Qué hace 'set -e' y por qué podría considerarse peligroso?


Respuestas:


154

set -e hace que el shell salga si algún subcomando o tubería devuelve un estado distinto de cero.

La respuesta que probablemente estaba buscando el entrevistador es:

Sería peligroso usar "set -e" al crear scripts init.d:

De http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 -

Tenga cuidado de usar set -e en los scripts init.d. Escribir scripts de init.d correctos requiere aceptar varios estados de salida de error cuando los demonios ya se están ejecutando o ya se han detenido sin abortar el script de init.d, y las bibliotecas de funciones comunes de init.d no son seguras para llamar con set -e en efecto. Para los scripts init.d, a menudo es más fácil no usar set -e y, en su lugar, verificar el resultado de cada comando por separado.

Esta es una pregunta válida desde el punto de vista del entrevistador porque evalúa a los candidatos con conocimientos prácticos de scripting y automatización a nivel de servidor


buen punto. detendría el proceso de arranque sobre algo que podría ser un error técnico, pero no debería hacer que todo el script se detenga
Matt

Aunque estoy de acuerdo con stackoverflow.com/questions/78497/… , creo que este estilo encuentra un buen ajuste con bash para verificaciones de inicio cortas y registros u otros parches de mono de entorno antes de ejecutar la carga de trabajo de destino. Utilizamos esta misma política para nuestra imagen acoplable guiones de inicio.
joshperry

33

De bash(1):

          -e      Exit immediately if a pipeline (which may consist  of  a
                  single  simple command),  a subshell command enclosed in
                  parentheses, or one of the commands executed as part  of
                  a  command  list  enclosed  by braces (see SHELL GRAMMAR
                  above) exits with a non-zero status.  The shell does not
                  exit  if  the  command that fails is part of the command
                  list immediately following a  while  or  until  keyword,
                  part  of  the  test  following  the  if or elif reserved
                  words, part of any command executed in a && or  ││  list
                  except  the  command  following  the final && or ││, any
                  command in a pipeline but the last, or if the  command’s
                  return  value  is being inverted with !.  A trap on ERR,
                  if set, is executed before the shell exits.  This option
                  applies to the shell environment and each subshell envi-
                  ronment separately (see  COMMAND  EXECUTION  ENVIRONMENT
                  above), and may cause subshells to exit before executing
                  all the commands in the subshell.

Desafortunadamente, no soy lo suficientemente creativo como para pensar por qué sería peligroso, aparte de "el resto del script no se ejecutará" o "posiblemente podría enmascarar problemas reales".


1
enmascarar verdadero / otros problemas, sí, supongo que me di cuenta de que ... yo estaba luchando para llegar a un ejemplo de que sea peligroso, así ...
cpbills

¡Ahí está la página de manual set! Siempre termino en builtin(1). Gracias.
Jared Beck

¿Cómo sabría que la documentación setdebe ser encontrada man 1 bash? y ¿cómo podría alguien encontrar la -eopción para la setpalabra clave en una página de manual que es tan enorme? no puedes /-ebuscar aquí
phil294

@Blauhirn usandohelp set
phil294 13/0318

19

Cabe señalar que set -ese puede activar y desactivar para varias secciones de un script. No tiene que estar activado para la ejecución de todo el script. Incluso podría habilitarse condicionalmente. Dicho esto, nunca lo uso ya que hago mi propio manejo de errores (o no).

some code
set -e     # same as set -o errexit
more code  # exit on non-zero status
set +e     # same as set +o errexit
more code  # no exit on non-zero status

También es digno de mención esto de la sección de la página del manual de Bash en el trapcomando que también describe cómo set -efunciona bajo ciertas circunstancias.

La trampa ERR no se ejecuta si el comando fallido es parte de la lista de comandos inmediatamente después de un tiempo o hasta la palabra clave, parte de la prueba en una declaración if, parte de un comando ejecutado en una lista && o ⎪⎪, o si el comando el valor de retorno se está invirtiendo a través de! Estas son las mismas condiciones obedecidas por la opción errexit.

Entonces, hay algunas condiciones bajo las cuales un estado distinto de cero no causará una salida.

Creo que el peligro está en no entender cuándo set -eentra en juego y cuándo no, y confiar en él incorrectamente bajo alguna suposición inválida.

Consulte también BashFAQ / 105 ¿Por qué set -e (o set -o errexit, o trap ERR) no hacen lo que esperaba?


Si bien me desvío un poco de la pregunta, esta respuesta es exactamente lo que estoy buscando: ¡desactivarla solo por una pequeña parte de un guión!
Stephan Bijzitter

13

Tenga en cuenta que este es un cuestionario para una entrevista de trabajo. Las preguntas pueden haber sido escritas por el personal actual y pueden estar equivocadas. Esto no es necesariamente malo, y todos cometen errores, y las preguntas de la entrevista a menudo se quedan en un rincón oscuro sin revisión, y solo salen durante una entrevista.

Es completamente posible que 'set -e' no haga nada que consideremos "peligroso". Pero el autor de esa pregunta puede creer erróneamente que 'set -e' es peligroso, debido a su propia ignorancia o parcialidad. Tal vez escribieron un script de shell con errores, bombardearon horriblemente, y erróneamente pensaron que 'culpable era' set -e ', cuando de hecho se negaron a escribir la verificación de errores adecuada.

He participado en probablemente 40 entrevistas de trabajo en los últimos 2 años, y los entrevistadores a veces hacen preguntas que están mal o tienen respuestas que están mal.

O tal vez es una pregunta capciosa, que sería poco convincente, pero no del todo inesperada.

O tal vez esta sea una buena explicación: http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html


1
Estoy en el mismo barco, y muchas de las entrevistas 'técnicas' con las que he estado tratando han estado al menos un poco equivocadas.
cpbills

1
Guau. Buen hallazgo en la lista de Debian. +1 por ignorancia de las preguntas de la entrevista. Recuerdo haber discutido una respuesta de netback una vez ya que estaba 100% seguro de que tenía razón. Dijeron no. Fui a casa y busqué en Google, tenía razón.
egorgry

9

set -e le dice a bash, en un script, que salga siempre que algo devuelva un valor de retorno distinto de cero.

Pude ver cómo sería molesto y con errores, no estoy seguro acerca de peligroso, a menos que haya abierto los permisos sobre algo, y antes de que pudiera restringirlos nuevamente, su script murió.


Eso es interesante. No pensé en los permisos abiertos en un script que muere prematuramente, pero pude ver que eso se considera peligroso. La parte divertida de este cuestionario es que no puede usar ningún material de referencia como man o google y si no puede responder completamente, no lo responda en absoluto.
egorgry

66
eso es una tontería, reconsideraría a este empleador ... bromeaba (más o menos). es bueno tener una base sólida de conocimiento, pero la mitad del trabajo de TI es saber / dónde / encontrar la información ... ojalá fueran lo suficientemente inteligentes como para tener eso en cuenta al calificar a los solicitantes. en una nota al margen, veo / no / uso porque set -e, de hecho, para mí habla de pereza para ignorar la comprobación de errores en sus scripts ... así que tenga esto en cuenta, también con este empleador ... si lo usan mucho, cómo son sus guiones, que inevitablemente tendrá que mantener ...
cpbills

Excelente punto @cpbills. Tampoco veo el uso de set -e en un script y probablemente sea por eso que me dejó perplejo. Me gustaría publicar todas las preguntas ya que algunas son realmente buenas y otras son realmente extrañas y aleatorias como esta.
egorgry

Lo he visto en muchos trabajos cron del sistema ...
Andrew

8

set -efinaliza el script si se encuentra un código de salida distinto de cero, excepto bajo ciertas condiciones. Para resumir los peligros de su uso en pocas palabras: no se comporta como la gente piensa que lo hace.

En mi opinión, debe considerarse como un hack peligroso que continúa existiendo solo con fines de compatibilidad. La set -edeclaración no convierte el shell de un lenguaje que usa códigos de error en un lenguaje que usa un flujo de control similar a una excepción, simplemente intenta mal emular ese comportamiento.

Greg Wooledge tiene mucho que decir sobre los peligros de set -e:

En el segundo enlace, hay varios ejemplos del comportamiento poco intuitivo e impredecible de set -e.

Algunos ejemplos del comportamiento poco intuitivo de set -e(algunos tomados del enlace wiki anterior):

  • conjunto -e
    x = 0
    dejar x ++
    echo "x es $ x"
    Lo anterior hará que el script de shell se cierre prematuramente, porque let x++devuelve 0, que la letpalabra clave trata como un valor falso y se convierte en un código de salida distinto de cero. set -ese da cuenta de esto y termina silenciosamente el script.
  • conjunto -e
    [-d / opt / foo] && echo "Advertencia: foo ya está instalado. Se sobrescribirá". > Y 2
    echo "Instalando foo ..."
    

    Lo anterior funciona como se esperaba, imprimiendo una advertencia si /opt/fooya existe.

    conjunto -e
    check_previous_install () {
        [-d / opt / foo] && echo "Advertencia: foo ya está instalado. Se sobrescribirá". > Y 2
    }
    
    check_previous_install
    echo "Instalando foo ..."
    

    Lo anterior, a pesar de que la única diferencia es que una sola línea se ha refactorizado en una función, finalizará si /opt/foono existe. Esto se debe a que el hecho de que funcionó originalmente es una excepción especial al set -ecomportamiento de. Cuando a && bdevuelve un valor distinto de cero, es ignorado por set -e. Sin embargo, ahora que es una función, el código de salida de la función es igual al código de salida de ese comando, y la función que devuelve un valor distinto de cero terminará silenciosamente el script.

  • conjunto -e
    IFS = $ '\ n' read -d '' -r -a config_vars <config
    

    Lo anterior leerá la matriz config_varsdel archivo config. Como el autor podría pretender, termina con un error si configfalta. Como el autor podría no tener la intención, termina silenciosamente si configno termina en una nueva línea. Si set -eno se usara aquí, config_varscontendría todas las líneas del archivo, ya sea que terminara o no en una nueva línea.

    Los usuarios de Sublime Text (y otros editores de texto que manejan líneas nuevas incorrectamente), tengan cuidado.

  • conjunto -e
    
    should_audit_user () {
        grupos de grupos locales = "$ (grupos" $ 1 ")"
        para grupo en $ grupos; hacer
            si ["$ grupo" = auditoría]; luego devuelve 0; fi
        hecho
        volver 1
    }
    
    if should_audit_user "$ usuario"; entonces
        registrador 'Blah'
    fi
    

    El autor aquí podría esperar razonablemente que si por alguna razón el usuario $userno existe, el groupscomando fallará y el script terminará en lugar de permitir que el usuario realice alguna tarea sin auditar. Sin embargo, en este caso la set -eterminación nunca surte efecto. Si $userno se puede encontrar por alguna razón, en lugar de terminar el script, la should_audit_userfunción simplemente devolverá datos incorrectos como si set -eno estuvieran vigentes.

    Esto se aplica a cualquier función invocada desde la parte de condición de una ifdeclaración, sin importar cuán profundamente anidada, sin importar dónde esté definida, sin importar si se ejecuta set -edentro de ella nuevamente. El uso ifen cualquier punto deshabilita completamente el efecto set -ehasta que el bloque de condición se ejecute completamente. Si el autor no es consciente de esta trampa, o no conoce toda su pila de llamadas en todas las situaciones posibles en las que se puede invocar una función, entonces escribirán un código defectuoso y la falsa sensación de seguridad proporcionada set -eserá al menos parcialmente culpa.

    Incluso si el autor es plenamente consciente de este escollo, la solución consiste en escribir el código de la misma manera en que se escribiría sin él set -e, haciendo que ese interruptor sea menos que inútil; el autor no solo tiene que escribir el código de manejo manual de errores como si set -eno estuviera vigente, sino que la presencia de los set -epuede haber engañado haciéndoles creer que no tienen que hacerlo.

Algunos inconvenientes adicionales de set -e:

  • Alienta el código descuidado. Los manejadores de errores se olvidan por completo, con la esperanza de que cualquier cosa que falle informe el error de alguna manera sensata. Sin embargo, con ejemplos como los let x++anteriores, este no es el caso. Si el script muere inesperadamente, generalmente es silencioso, lo que dificulta la depuración. Si el script no muere y esperaba que lo hiciera (vea el punto anterior), entonces tiene un error más sutil e insidioso en sus manos.
  • Lleva a las personas a una falsa sensación de seguridad. Ver de nuevo la ifviñeta de condición.
  • Los lugares donde termina el shell no son consistentes entre shells o versiones de shell. Es posible escribir accidentalmente un script que se comporta de manera diferente en una versión anterior de bash debido al comportamiento de set -ehaber sido modificado entre esas versiones.

set -ees un tema polémico, y algunas personas conscientes de los problemas que lo rodean recomiendan no hacerlo, mientras que otros simplemente recomiendan tener cuidado mientras están activos para conocer las trampas. Hay muchos novatos de scripting de shell que recomiendan set -een todos los scripts como un todo para condiciones de error, pero en la vida real no funciona de esa manera.

set -e No es un sustituto de la educación.


2

Diría que es peligroso porque ya no controlas el flujo de tu guión. El script puede finalizar siempre que cualquiera de los comandos que invoca devuelva un valor distinto de cero. Entonces, todo lo que tiene que hacer es hacer algo que altere el comportamiento o la salida de cualquiera de los componentes, y puede terminar el script principal. Puede ser más un problema de estilo, pero definitivamente tiene consecuencias. ¿Qué pasaría si ese script principal tuyo supusiera establecer alguna bandera, y no lo hizo porque terminó antes? Terminaría fallando el resto del sistema si supone que la bandera debería estar allí, o trabajando con un valor predeterminado o antiguo inesperado.


Totalmente de acuerdo con esta respuesta. No es inherentemente peligroso, pero es una oportunidad para tener una salida anticipada inesperada.
pboin

1
Lo que esto me recordó es un profesor mío en C ++ que siempre estaba sacando puntos de mis tareas por no tener un solo punto de entrada / punto de salida único en un programa. Pensé que era puramente un "principio", pero este negocio conjunto definitivamente demuestra cómo y por qué las cosas pueden salir completamente de control. En última instancia, los programas tienen que ver con el control y el determinismo, y con la terminación prematura, renuncia a ambos.
Marcin

0

Como un ejemplo más concreto de la respuesta de @ Marcin que personalmente me ha mordido, imagine que hay una rm foo/bar.txtlínea en algún lugar de su guión. Por lo general, no es gran cosa si en foo/bar.txtrealidad no existe. Sin embargo, con set -epresente ahora, su secuencia de comandos terminará temprano allí Ups

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.