GNU/Linux >> Tutoriales Linux >  >> Linux

¿Errores de captura en la sustitución de comandos usando "-o Errtrace" (es decir, establecer -e)?

Según este manual de referencia:

-E (también -o errtrace)

Si se establece, cualquier trampa en ERR es heredada por funciones de shell, sustituciones de comandos y comandos ejecutados en un entorno de subshell. La trampa
ERR normalmente no se hereda en tales casos.

Sin embargo, debo estar interpretándolo mal, 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 conozco la asignación simple, my_var=$(made_up_name) , saldrá del script con set -e (es decir, errexit).

Es -E/-o errtrace ¿Se supone que funciona como el código anterior? ¿O, lo más probable, lo leí mal?

Respuesta aceptada:

Nota:zsh se quejará de "malos patrones" 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 de proxy como lo he hecho con sh <<-CMD .

Ok, entonces, como dije en los comentarios anteriores, no sé específicamente sobre el set -E de bash , pero sé que los shells compatibles con POSIX brindan un medio simple de probar un valor si lo desea:

    sh -evx <<-CMD
    _test() { echo $( ${empty:?error string} ) &&
        echo "echo still works" 
    }
    _test && echo "_test doesnt fail"
    # END
    CMD
sh: line 1: empty: error string
+ echo

+ echo 'echo still works'
echo still works
+ echo '_test doesnt fail'
_test doesnt fail

Arriba verá que aunque usé parameter expansion para probar ${empty?} _test() todavía return s un pase, como se evidencia en el último echo Esto ocurre porque el valor fallido mata el $( command substitution ) subshell que lo contiene, pero su shell principal:_test en este momento - sigue en camiones. Y echo no le importa:está muy feliz de servir solo un newline; echo es no una prueba.

Pero considera esto:

    sh -evx <<-CMD
    _test() { echo $( ${empty:?error string} ) &&
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    _test ||
            echo "this doesnt even print"
    # END
    CMD
_test+ sh: line 1: empty: function doesnt run

Porque alimenté _test()'s entrada con un parámetro preevaluado en el INIT here-document ahora el _test() La función ni siquiera intenta ejecutarse en absoluto. Además, el sh Shell aparentemente se da por vencido por completo y echo "this doesnt even print" ni siquiera se imprime.

Probablemente eso no lo que quieras.

Esto sucede porque ${var?} La expansión de parámetros de estilo está diseñada para salir del shell en caso de que falte un parámetro, funciona así:

${parameter:?[word]}

Indicar error si Null o Unset. Si el parámetro no está establecido o es nulo, la expansion of word (o un mensaje que indique que no está configurado si se omite la palabra) se written to standard error y el shell exits with a non-zero exit status . De lo contrario, se sustituirá el valor del parámetro parameter shall be substituted . Un shell interactivo no necesita salir.

No copiaré/pegaré todo el documento, pero si desea un error para un set but null valor usas el formulario:

${var 😕 error message }

Con los :colon como anteriormente. Si desea un null valor para tener éxito, simplemente omita los dos puntos. También puede negarlo y fallar solo para valores establecidos, como mostraré en un momento.

Otra ejecución de _test():

    sh <<-CMD
    _test() { echo $( ${empty:?error string} ) &&
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    echo "this runs" |
        ( _test ; echo "this doesnt" ) ||
            echo "now it prints"
    # END
    CMD
this runs
sh: line 1: empty: function doesnt run
now it prints

Esto funciona con todo tipo de pruebas rápidas, pero arriba verás que _test() , ejecutar desde la mitad de la pipeline falla, y de hecho contiene command list subshell falla por completo, ya que ninguno de los comandos dentro de la función se ejecuta ni el siguiente echo ejecutar en absoluto, aunque también se muestra que se puede probar fácilmente porque echo "now it prints" ahora imprime.

El diablo está en los detalles, supongo. En el caso anterior, el shell que sale es no el script _main | logic | pipeline pero el ( subshell in which we ${test?} ) || por lo que se requiere un poco de sandboxing.

Relacionado:No puedo abrir una aplicación como root. ¿No se encuentra el comando Sudo?

Y puede que no sea obvio, pero si quisieras pasar solo por el caso contrario, o solo set= valores, también es bastante simple:

    sh <<-CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    )
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    )
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
    CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

El ejemplo anterior aprovecha las 4 formas de sustitución de parámetros POSIX y sus diversos :colon null o not null pruebas Hay más información en el enlace de arriba, y aquí está de nuevo.

Y supongo que deberíamos mostrar nuestro _test función de trabajo, también, ¿verdad? Simplemente declaramos empty=something como parámetro de nuestra función (o en cualquier momento antes):

    sh <<-CMD
    _test() { echo $( echo ${empty:?error string} ) &&
            echo "echo still works" ; } 2<<-INIT
            ${empty?tested as a pass before function runs}
    INIT
    echo "this runs" >&2 |
        ( empty=not_empty _test ; echo "yay! I print now!" ) ||
            echo "suspiciously quiet"
    # END
    CMD
this runs
not_empty
echo still works
yay! I print now!

Cabe señalar que esta evaluación es independiente:no requiere ninguna prueba adicional para fallar. Un par de ejemplos más:

    sh <<-CMD
    empty= 
    ${empty?null, no colon, no failure}
    unset empty
    echo "${empty?this is stderr} this is not"
    # END
    CMD
sh: line 3: empty: this is stderr

    sh <<-CMD
    _input_fn() { set -- "[email protected]" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    }
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
    CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?

Y finalmente volvemos a la pregunta original:cómo manejar los errores en un $(command substitution) subcapa? 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 (incluidas $(command substitution) ) ocurren antes en el proceso de evaluación del shell que en la ejecución actual del comando del shell, que es cuando sus errores pueden detectarse y atraparse.

El problema que experimenta la operación es que en el momento en que el shell actual evalúa los errores, el $(command substitution) la subcapa ya se ha sustituido; no quedan errores.

Entonces, ¿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.

Relacionado:¿La declaración PostgreSQL Select produce errores en datagrip pero no en pgAdmin4?

Método 1:

    echo "$(madeup && echo : || echo '${fail:?die}')" |
          . /dev/stdin

sh: command not found: madeup
/dev/stdin:1: fail: die

    echo $?

126

Método 2:

    var="$(madeup)" ; echo "${var:?die} still not stderr"

sh: command not found: madeup
sh: var: die

    echo $?

1

Esto
fallará independientemente del número de variables declaradas por línea:

   v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"

sh: command not found: madeup
sh: v1: parameter not set

Y nuestro valor de retorno permanece constante:

    echo $?
1

AHORA LA TRAMPA:

    trap 'printf %s\n trap resurrects shell!' ERR
    v1="$(madeup)" v2="$(printf %s\n shown after trap)"
    echo "${v1:?#1 - still stderr}" "${v2:?invisible}"

sh: command not found: madeup
sh: v1: #1 - still stderr
trap
resurrects
shell!
shown
after
trap

    echo $?
0

Linux
  1. Sustitución de comandos:¿Dividir en nueva línea pero no en espacio?

  2. Sustitución de comandos usando “?

  3. ¿Usando exportar en .bashrc?

  4. ¿Cambiar fuente en Echo Command?

  5. Comando de eco de Linux

16 ejemplos de comandos de eco en Linux

Comando Echo en Linux con ejemplos

Comando echo en Linux:7 ejemplos prácticos

Usando el comando Watch en Linux

Usando el comando tr en Linux para jugar con personajes

¿Cómo insertar una nueva línea en el correo electrónico usando el comando de correo de Linux?