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


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

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


141
2018-05-19 16:39


origen


Aquí hay un tema relacionado: stackoverflow.com/questions/64786/error-handling-in-bash/… - Stefan Lasiewski


Respuestas:


set -e hace que el shell se cierre si algún subcomando o canalización devuelve un estado distinto de cero.

La respuesta que el entrevistador probablemente estaba buscando es:

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

Desde 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 las secuencias de comandos init.d correctas requiere aceptar varios estados de salida de error cuando los daemons ya se están ejecutando o ya se han detenido sin abortar la secuencia de comandos init.d, y las bibliotecas de funciones init.d comunes no son seguras de llamar con set -e en efecto. Para los scripts init.d, a menudo es más fácil no usar set -e y, en cambio, verifique el resultado de cada comando por separado.

Esta es una pregunta válida desde el punto de vista de un entrevistador porque evalúa el conocimiento de trabajo de los candidatos sobre automatización y scripting a nivel de servidor.


146
2017-08-09 21:01



buen hallazgo Gracias. - egorgry
buen punto. detendría el proceso de arranque por algo que podría ser técnicamente un error, 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 encaja bien con bash para verificaciones cortas de inicio y registro u otros parches de entorno antes de ejecutar la carga de trabajo de destino. Usamos esta misma política para nuestros scripts de inicio de imagen de docker. - joshperry


Desde 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 que "el resto de la secuencia de comandos no se ejecutará" o "posiblemente podría enmascarar problemas reales".


33
2018-05-19 16:43



Enmascarar problemas reales / otros, sí, supongo que podría ver que ... estaba luchando para dar un ejemplo de que sea peligroso, también ... - cpbills
Ahí está la página del manual para set! Siempre termino en builtin(1). Gracias. - Jared Beck
¿Cómo sabría que la documentación para set se encuentra en man 1 bash?? y ¿cómo podría alguien encontrar el -e opción para el set palabra clave en una página de manual que es tan enormemente grande? no puedes simplemente /-e busca aquí - Blauhirn
@Blauhirn usando help set - Blauhirn


se debe notar que set -e se puede activar y desactivar para varias secciones de un script. No tiene que estar encendido para la ejecución de todo el script. Incluso podría ser condicionalmente habilitado. 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 de Bash en la trap comando que también describe cómo set -e Funciona bajo ciertas circunstancias.

los                 ERR trap no se ejecuta si el comando fallido es parte de la                 lista de comandos inmediatamente después de un tiempo o hasta que la palabra clave,                 parte de la prueba en una sentencia if, parte de un comando ejecutado                 en una lista && o ⎪⎪, o si el valor de retorno del comando está siendo                 Vía invertida! Estas son las mismas condiciones que obedece la                 Opción errexit.

Así que 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 -e entra en juego y cuando no lo hace y confiando en él incorrectamente bajo algún supuesto no válido.

Por favor vea también BashFAQ / 105 ¿Por qué set -e (o set -o errexit, o trap ERR) hace lo que esperaba?


19
2018-05-19 22:39



Aunque 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


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 sientan en un rincón oscuro sin revisión, y solo salen durante una entrevista.

Es totalmente 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 sesgo. Tal vez escribieron un script de shell defectuoso, lo bombardearon horriblemente, y pensaron erróneamente que 'set -e' era la culpa, cuando en realidad se olvidaron de escribir el correcto control de errores.

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

O tal vez es una pregunta trampa, que sería aburrida, pero no del todo inesperada.

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


13
2018-05-19 17:03



+1 estoy en el mismo barco, y muchas de las entrevistas "técnicas" con las que he estado tratando han sido, al menos, un poco equivocadas. - cpbills
Guau. Buen hallazgo en la lista debian. +1 por la 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. Ellos dijeron que no. Fui a casa y lo busqué en Google, tenía razón. - egorgry


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

Podía ver cómo eso sería molesto y con muchos errores, no estoy seguro de que sea peligroso, a menos que hayas abierto los permisos para algo, y antes de que pudieras restringirlos de nuevo, tu script murió.


9
2018-05-19 16:46



Eso es interesante. No pensé en los permisos abiertos en un script que muere prematuramente, pero pude ver que se consideraba 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 por completo, no lo responda en absoluto. - egorgry
eso es una tontería, reconsideraría a este empleador ... bromeando (más o menos). es bueno tener una base sólida de conocimientos, pero la mitad del trabajo de TI es saber / dónde / encontrar la información ... con suerte, fueron lo suficientemente inteligentes como para tener eso en cuenta al calificar a los solicitantes. en una nota lateral, veo / no / uso para set -eDe hecho, para mí, habla de pereza ignorar el control de errores en sus scripts ... así que tenga esto en cuenta, también con este empleador ... si lo usan mucho, ¿cómo se ven sus scripts? inevitablemente habrá que mantener ... - cpbills
Excelente punto @cpbills. Tampoco veo ningún uso para configurar -e en un script y es probablemente la razón por la que me sorprendió tanto. Me gustaría publicar todas las preguntas, ya que algunas son realmente buenas y otras son realmente alocadas y aleatorias como esta. - egorgry
Lo he visto en muchos trabajos cron del sistema ... - Andrew


set -e termina la secuencia de comandos 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, debería considerarse como un pirateo peligroso que continúa existiendo solo por motivos de compatibilidad. los set -e La declaració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 emular ese comportamiento.

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

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

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

  • establecer -e
    x = 0
    deja x ++
    echo "x es $ x"
    Lo anterior hará que el script de shell se cierre prematuramente, porque let x++ devuelve 0, que es tratado por el let palabra clave como un valor falso y se convirtió en un código de salida distinto de cero. set -e nota esto, y termina silenciosamente el script.
  • establecer -e
    [-d / opt / foo] && echo "Advertencia: foo ya está instalado. Se sobrescribirá". > & 2
    echo "instalando foo ..."
    

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

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

    Lo anterior, a pesar de que la única diferencia es que una sola línea se ha rediseñado en una función, terminará si /opt/foo no existe. Esto se debe a que el hecho de que funcionó originalmente es una excepción especial a set -eel comportamiento de Cuando a && b devuelve un valor distinto de cero, se ignora 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á de manera silenciosa la secuencia de comandos.

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

    Lo anterior leerá la matriz. config_vars del archivo config. Como el autor podría intentar, termina con un error si config Está perdido. Como el autor podría no querer, termina en silencio si config No termina en una nueva línea. Si set -e no fueron utilizados aquí, entonces config_vars contendría todas las líneas del archivo, ya sea que termine o no en una nueva línea.

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

  • establecer -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 "$ user"; entonces
        logger 'Blah'
    fi
    

    El autor aquí podría razonablemente esperar que si por alguna razón el usuario $user no existe, entonces el groups El comando fallará y la secuencia de comandos terminará en lugar de permitir que el usuario realice una tarea no auditada. Sin embargo, en este caso el set -e La terminación nunca tiene efecto. Si $user no se puede encontrar por alguna razón, en lugar de terminar el script, el should_audit_user La función simplemente devolverá datos incorrectos como si set -e no estaba en efecto

    Esto se aplica a cualquier función invocada desde la parte de condición de un if declaración, no importa cuán profundamente anidada, no importa dónde esté definida, no importa incluso si ejecuta set -e dentro de nuevo Utilizando if en cualquier punto desactiva completamente el efecto de set -e Hasta que el bloque de condición se ejecute completamente. Si el autor no está al tanto de este error, o no conoce la totalidad de su pila de llamadas en todas las situaciones posibles en las que se puede llamar a una función, escribirán el código con errores y la falsa sensación de seguridad proporcionada por set -e Será al menos parcialmente la culpa.

    Incluso si el autor es plenamente consciente de este error, la solución consiste en escribir el código de la misma manera que uno lo escribiría sin set -e, efectivamente haciendo que el interruptor sea menos que inútil; El autor no solo tiene que escribir el código de manejo manual de errores como si set -e no estaban vigentes, pero la presencia de set -e puede haberlos engañado haciéndoles creer que no tienen que hacerlo.

Algunos inconvenientes adicionales de set -e:

  • Se fomenta el código descuidado. Los manejadores de errores se olvidan por completo, con la esperanza de que, en caso de fallar, se informe el error de alguna manera sensata. Sin embargo, con ejemplos como let x++ arriba, 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 usted esperaba que (vea el punto anterior), entonces tiene un error más sutil e insidioso en sus manos.
  • Conduce a las personas a una falsa sensación de seguridad. Ver de nuevo el if-condición de punto de bala.
  • Los lugares donde termina la shell no son consistentes entre shells o versiones de shell. Es posible escribir accidentalmente una secuencia de comandos que se comporta de manera diferente en una versión anterior de bash debido al comportamiento de set -e Habiendo sido ajustado entre esas versiones.

set -e es un tema polémico, y algunas personas conscientes de los problemas que lo rodean lo recomiendan para evitarlo, mientras que otras solo recomiendan tener cuidado mientras está activo para conocer las trampas. Hay muchos novatos en shell scripting que recomiendan set -een todos los scripts como un catch-all para las condiciones de error, pero en la vida real no funciona de esa manera.

set -e No hay sustituto para la educación.


5
2018-04-27 23:06





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


2
2018-05-19 18:07



Totalmente de acuerdo con esta respuesta. No es inherentemente Peligroso, pero es una oportunidad para tener una salida anticipada inesperada. - pboin
Lo que me recordó es un profesor de C ++ que siempre estaba sacando puntos de mis tareas por no tener un punto de entrada único / punto de salida único en un programa. Pensé que era simplemente una cuestión de "principios", pero este conjunto de negocios definitivamente demuestra cómo y por qué las cosas pueden salirse de control. En última instancia, los programas tienen que ver con el control y el determinismo, y con una terminación prematura, renuncias a ambos. - Marcin