GNU/Linux >> Linux Esercitazione >  >> Linux

Errori di intercettazione nella sostituzione dei comandi utilizzando "-o Errtrace" (ad es. Set -e)?

Secondo questo manuale di riferimento:

-E (anche -o errtrace)

Se impostata, qualsiasi trap su ERR viene ereditata da funzioni shell, sostituzioni di comandi e comandi eseguiti in un ambiente subshell. La trap
ERR normalmente non viene ereditata in questi casi.

Tuttavia, devo interpretarlo in modo errato, perché quanto segue non funziona:

#!/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 ! "

Conosco già un semplice compito, my_var=$(made_up_name) , uscirà dallo script con set -e (cioè errexit).

-E/-o errtrace dovrebbe funzionare come il codice sopra? O, molto probabilmente, l'ho letto male?

Risposta accettata:

Nota:zsh si lamenterà di "modelli errati" se non lo configuri per accettare "commenti in linea" per la maggior parte degli esempi qui e non li esegui tramite una shell proxy come ho fatto con sh <<-CMD .

Ok, quindi, come ho affermato nei commenti sopra, non conosco in modo specifico il set -E di bash , ma so che le shell compatibili con POSIX forniscono un semplice mezzo per testare un valore se lo desideri:

    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

Sopra lo vedrai anche se ho usato parameter expansion per testare ${empty?} _test() ancora return s un pass – come si evince dall'ultimo echo Ciò si verifica perché il valore non riuscito elimina il $( command substitution ) subshell che lo contiene, ma la sua shell madre – _test in questo momento – continua ad autotrasportare. E echo non importa:è molto felice di servire solo un newline; echo è non una prova.

Ma considera questo:

    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

Perché ho dato da mangiare a _test()'s input con un parametro prevalutato nel INIT here-document ora il _test()'s la funzione non tenta nemmeno di essere eseguita. Inoltre, il sh apparentemente la shell rinuncia completamente al fantasma e echo "this doesnt even print" non stampa nemmeno.

Probabilmente non quello che vuoi.

Ciò accade perché ${var?} l'espansione dei parametri di stile è progettata per uscire dalla shell in caso di parametro mancante, funziona così:

${parameter:?[word]}

Indica Errore se Null o Unset. Se il parametro non è impostato o è nullo, l'expansion of word (o un messaggio che indica che non è impostato se la parola è omessa) deve essere written to standard error e la shell exits with a non-zero exit status . In caso contrario, verrà sostituito il valore del parameter shall be substituted . Non è necessario che una shell interattiva esca.

Non copierò/incollerò l'intero documento, ma se vuoi un errore per un set but null valore si utilizza il modulo:

${var 😕 error message }

Con il :colon come sopra. Se vuoi un null valore per avere successo, basta omettere i due punti. Puoi anche negarlo e fallire solo per valori impostati, come mostrerò tra poco.

Un'altra esecuzione di _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

Funziona con tutti i tipi di test rapidi, ma sopra vedrai che _test()'s , esegui dal centro della pipeline fallisce, e in effetti contiene command list subshell fallisce completamente, poiché nessuno dei comandi all'interno della funzione viene eseguito né il seguente echo eseguito, sebbene sia anche dimostrato che può essere facilmente testato perché echo "now it prints" ora stampa.

Il diavolo è nei dettagli, credo. Nel caso precedente, la shell che esce non _main | logic | pipeline ma il ( subshell in which we ${test?} ) || quindi è necessario un po' di sandboxing.

Correlati:impossibile aprire un'app come root. Comando Sudo non trovato?

E potrebbe non essere ovvio, ma se volessi passare solo per il caso opposto, o solo set= valori, è anche abbastanza semplice:

    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

L'esempio precedente sfrutta tutte e 4 le forme di sostituzione dei parametri POSIX e i loro vari :colon null o not null prove. Ci sono ulteriori informazioni nel link sopra, ed eccolo di nuovo qui.

E suppongo che dovremmo mostrare il nostro _test anche la funzione funziona, giusto? Dichiariamo semplicemente empty=something come parametro per la nostra funzione (o in qualsiasi momento in anticipo):

    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!

Va notato che questa valutazione è isolata:non richiede test aggiuntivi per fallire. Un altro paio di esempi:

    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?

E così finalmente torniamo alla domanda originale:come gestire gli errori in un $(command substitution) subshell? La verità è che ci sono due modi, ma nessuno dei due è diretto. Il cuore del problema è il processo di valutazione della shell:espansioni della shell (incluso $(command substitution) ) si verificano prima nel processo di valutazione della shell rispetto all'esecuzione corrente dei comandi della shell, ovvero quando i tuoi errori potrebbero essere rilevati e intrappolati.

Il problema riscontrato dall'op è che quando la shell corrente valuta gli errori, $(command substitution) la subshell è già stata sostituita:non rimangono errori.

Allora quali sono i due modi? O lo fai esplicitamente all'interno di $(command substitution) subshell con test come faresti senza di essa, oppure ne assorbi i risultati in una variabile di shell corrente e ne verifichi il valore.

Correlati:l'istruzione PostgreSQL Select produce errori in datagrip ma non pgAdmin4?

Metodo 1:

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

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

    echo $?

126

Metodo 2:

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

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

    echo $?

1

Questo
fallirà indipendentemente dal numero di variabili dichiarate per riga:

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

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

E il nostro valore di ritorno rimane costante:

    echo $?
1

ORA LA TRAPPOLA:

    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. Sostituzione del comando:divisione su una nuova riga ma non nello spazio?

  2. Sostituzione dei comandi usando “?

  3. Utilizzo di Esporta in .bashrc?

  4. Cambia carattere nel comando Echo?

  5. Come impostare le attività di automazione su un VPS Linux utilizzando Cron?

16 Esempi di comandi Echo in Linux

Echo Command in Linux con esempi

echo Command in Linux:7 esempi pratici

Utilizzo del comando Watch in Linux

Usare il comando tr in Linux per giocare con i personaggi

Come inserire una nuova riga nell'e-mail usando il comando linux mail?