De acuerdo con este manual de referencia :
-E (también -o errtrace)
Si se establece, cualquier trampa en ERR es heredada por las funciones de shell, las sustituciones de comandos y los comandos ejecutados en un entorno de subshell. La trampa ERR normalmente no se hereda en tales casos.
Sin embargo, debo interpretarlo incorrectamente, porque lo siguiente no funciona:
#!/usr/bin/env bash
# -*- bash -*-
set -e -o pipefail -o errtrace -o functrace
function boom {
echo "err status: $?"
exit $?
}
trap boom ERR
echo $( made up name )
echo " ! should not be reached ! "
Ya sé de asignación simple my_var=$(made_up_name)
, saldrá del script con set -e
(es decir, errexit).
¿Se -E/-o errtrace
supone que funciona como el código anterior? O, lo más probable, ¿lo he leído mal?
bash
command-substitution
dgo.a
fuente
fuente
echo $( made up name )
con$( made up name )
produce el comportamiento deseado. Sin embargo, no tengo una explicación.var=$( pipe )
y$( pipe )
ejemplos representarían puntos finales de tubería, mientraspipe > echo
que no lo haría. Mi página hombre dice: "1. El fracaso de cualquier comando individual en una tubería de varios comandos no causará la cáscara para salir Sólo el fracaso de la tubería en sí serán considerados.."echo
siempre devuelve 0. Esto debe tenerse en cuenta en el análisis ...Respuestas:
Nota:
zsh
se quejará de "patrones incorrectos" si no lo configura para aceptar "comentarios en línea" para la mayoría de los ejemplos aquí y no los ejecuta a través de un shell proxy como lo he hechosh <<-\CMD
.De acuerdo, como dije en los comentarios anteriores, no sé específicamente sobre bash
set -E
, pero sé que los shells compatibles con POSIX proporcionan un medio simple de probar un valor si lo desea:Arriba verá que, aunque solía
parameter expansion
probar,${empty?} _test()
todavíareturn
es un paso, como se evidencia en el último.echo
Esto ocurre porque el valor fallido mata la$( command substitution )
subshell que lo contiene, pero su shell principal,_test
en este momento, continúa transportándose en camiones. Yecho
no le importa, está muy feliz de servir solo y no\newline; echo
es una prueba.Pero considere esto:
Debido a que introduje la
_test()'s
entrada con un parámetro previamente evaluado en elINIT here-document
ahora, la_test()
función ni siquiera intenta ejecutarse en absoluto. Además, elsh
caparazón aparentemente abandona el fantasma por completo yecho "this doesnt even print"
ni siquiera imprime.Probablemente eso no sea lo que quieres.
Esto sucede porque el
${var?}
estilo de expansión de parámetros está diseñado para salirshell
en caso de que falte un parámetro, funciona así :No copiaré / pegaré todo el documento, pero si desea una falla para un
set but null
valor, use el formulario:Con lo de
:colon
arriba. Si desea que unnull
valor tenga éxito, simplemente omita los dos puntos. También puede negarlo y fallar solo para los valores establecidos, como lo mostraré en un momento.Otra carrera de
_test():
Esto funciona con todo tipo de pruebas rápidas, pero arriba verá que se
_test()
ejecuta desde la mitad de lospipeline
errores y, de hecho, sucommand list
subshell que falla falla por completo, ya que ninguno de los comandos dentro de la función se ejecuta ni la siguienteecho
ejecución, aunque también se muestra que puede probarse fácilmente porqueecho "now it prints"
ahora imprime.El diablo está en los detalles, supongo. En el caso anterior, el shell que sale no es el script
_main | logic | pipeline
sino( subshell in which we ${test?} ) ||
que se requiere un pequeño sandboxing.Y puede que no sea obvio, pero si solo desea pasar por el caso opuesto, o solo los
set=
valores, también es bastante simple:El ejemplo anterior aprovecha las 4 formas de sustitución de parámetros POSIX y sus diversas
:colon null
onot null
pruebas. Hay más información en el enlace de arriba, y aquí está de nuevo .Y supongo que también deberíamos mostrar el
_test
funcionamiento de nuestra función, ¿verdad? Simplemente declaramosempty=something
como parámetro a nuestra función (o en cualquier momento de antemano):Cabe señalar que esta evaluación es independiente: no requiere ninguna prueba adicional para fallar. Un par de ejemplos más:
Y finalmente volvemos a la pregunta original: ¿cómo manejar los errores en una
$(command substitution)
subshell? La verdad es que hay dos formas, pero ninguna es directa. El núcleo del problema es el proceso de evaluación del shell: las expansiones del shell (incluido$(command substitution)
) ocurren antes en el proceso de evaluación del shell que la ejecución actual del comando del shell, que es cuando sus errores pueden ser atrapados y atrapados.El problema que experimenta el operador operativo es que cuando el shell actual evalúa los errores, el
$(command substitution)
subshell ya se ha sustituido, no quedan errores.¿Cuáles son las dos formas? O lo hace explícitamente dentro del
$(command substitution)
subshell con pruebas como lo haría sin él, o absorbe sus resultados en una variable de shell actual y prueba su valor.Método 1:
Método 2:
Esto fallará independientemente del número de variables declaradas por línea:
Y nuestro valor de retorno permanece constante:
AHORA LA TRAMPA:
fuente
echo "abc" "${v1:?}"
no parece ejecutarse (abc nunca se imprime). Y el shell devuelve 1. Esto es cierto con o sin un comando par ("${v1:?}"
directamente en el cli). Pero para el guión de OP, todo lo que se requería para desencadenar la trampa es colocar su asignación variable que contiene una sustitución por un comando inexistente solo en una sola línea. De lo contrario, el comportamiento del eco es devolver 0 siempre, a menos que se interrumpa como con las pruebas que explicó.v=$( madeup )
. No veo qué es inseguro con eso. Es solo una tarea, la persona escribió mal el comando, por ejemplov="$(lss)"
. Se equivoca. Sí, puede verificar con el estado de error del último comando $? - porque es el comando en la línea (una asignación sin nombre de comando) y nada más - no es un argumento para repetir. Además, aquí está atrapado por la función como! = 0, por lo que recibe comentarios dos veces. De lo contrario, seguramente, como explica, hay una mejor manera de hacer esto de manera ordenada dentro de un marco, pero OP tenía 1 sola línea: un eco más su sustitución fallida. Se preguntaba sobre el eco.En su script es una ejecución de comando (
echo $( made up name )
). En bash los comandos están delimitados con cualquiera ; o con nueva linea . En el comando$( made up name )
se considera como parte del comando. Incluso si esta parte falla y regresa con error, todo el comando se ejecuta con éxito yaecho
que no lo sé. Cuando el comando regresa con 0, no se activa ninguna trampa.Necesitas ponerlo en dos comandos, asignación y eco
fuente
echo $var
no falla:$var
se expande para vaciarse antes deecho
echarle un vistazo.Esto se debe a un error en bash. Durante la sustitución de comandos, ingrese
made
se ejecuta (o no se puede encontrar) en un subshell, pero el subshell está "optimizado" de tal manera que no utiliza algunas trampas del shell principal. Esto se solucionó en la versión 4.4.5 :Con bash 4.4.5 o superior, debería ver el siguiente resultado:
El controlador de trampa se ha llamado como se esperaba, luego se cierra la subshell. (
set -e
solo hace que la subshell salga, no el padre, de modo que el mensaje "no debería ser alcanzado" debería, de hecho, ser alcanzado).Una solución para las versiones anteriores es forzar la creación de una subshell completa y no optimizada:
Los espacios adicionales son necesarios para distinguir de la expansión aritmética.
fuente