GNU/Linux >> Tutoriels Linux >  >> Linux

Intercepter les erreurs dans la substitution de commande à l'aide de "-o Errtrace" (c'est-à-dire Set -e) ?

Selon ce manuel de référence :

-E (également -o errtrace)

S'il est défini, tout piège sur ERR est hérité par les fonctions du shell, les substitutions de commandes et les commandes exécutées dans un environnement de sous-shell. Le
piège ERR n'est normalement pas hérité dans de tels cas.

Cependant, je dois mal l'interpréter, car ce qui suit ne fonctionne pas :

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

Je connais déjà l'affectation simple, my_var=$(made_up_name) , quittera le script avec set -e (c'est-à-dire errexit).

Est-ce que -E/-o errtrace censé fonctionner comme le code ci-dessus? Ou, très probablement, j'ai mal lu ?

Réponse acceptée :

Remarque :zsh se plaindra de "mauvais modèles" si vous ne le configurez pas pour accepter les "commentaires en ligne" pour la plupart des exemples ici et ne les exécutez pas via un proxy shell comme je l'ai fait avec sh <<-CMD .

Ok, donc, comme je l'ai dit dans les commentaires ci-dessus, je ne connais pas spécifiquement le set -E de bash , mais je sais que les shells compatibles POSIX fournissent un moyen simple de tester une valeur si vous le souhaitez :

    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

Ci-dessus, vous verrez que j'ai utilisé parameter expansion pour tester ${empty?} _test() toujours return s une réussite - comme en témoigne le dernier echo Cela se produit parce que la valeur d'échec tue le $( command substitution ) sous-shell qui le contient, mais son shell parent – ​​_test en ce moment - continue de camionner. Et echo s'en fiche - c'est très heureux de ne servir qu'un newline; echo n'est pas un essai.

Mais considérez ceci :

    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

Parce que j'ai nourri les _test()'s entrée avec un paramètre pré-évalué dans le INIT here-document maintenant le _test() la fonction n'essaie même pas de s'exécuter du tout. De plus, le sh shell abandonne apparemment complètement le fantôme et echo "this doesnt even print" n'imprime même pas.

Ce n'est probablement pas ce que vous voulez.

Cela se produit parce que le ${var?} style parameter-expansion est conçu pour quitter le shell en cas de paramètre manquant, cela fonctionne comme ceci :

${parameter:?[word]}

Indiquer une erreur si Null ou Unset. Si le paramètre n'est pas défini ou est nul, l'expansion of word (ou un message indiquant qu'il n'est pas défini si le mot est omis) doit être written to standard error et le shell exits with a non-zero exit status . Sinon, la valeur du paramètre parameter shall be substituted . Un shell interactif n'a pas besoin de se fermer.

Je ne vais pas copier/coller tout le document, mais si vous voulez un échec pour un set but null valeur vous utilisez le formulaire :

${var 😕 error message }

Avec le :colon comme ci-dessus. Si vous voulez un null valeur pour réussir, omettez simplement les deux-points. Vous pouvez également l'annuler et échouer uniquement pour les valeurs définies, comme je le montrerai dans un instant.

Une autre exécution 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

Cela fonctionne avec toutes sortes de tests rapides, mais ci-dessus, vous verrez que _test() , exécuté à partir du milieu du pipeline échoue, et en fait il contient command list le sous-shell échoue complètement, car aucune des commandes de la fonction ne s'exécute ni le echo suivant exécuter du tout, bien qu'il soit également démontré qu'il peut facilement être testé car echo "now it prints" imprime maintenant.

Le diable est dans les détails, je suppose. Dans le cas ci-dessus, le shell qui sort n'est pas le _main | logic | pipeline mais le ( subshell in which we ${test?} ) || donc un peu de bac à sable s'impose.

En relation :Impossible d'ouvrir une application en tant que root. Commande sudo introuvable ?

Et ce n'est peut-être pas évident, mais si vous vouliez passer uniquement pour le cas contraire, ou uniquement set= valeurs, c'est assez simple aussi :

    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'exemple ci-dessus tire parti des 4 formes de substitution de paramètres POSIX et de leurs différents :colon null ou not null essais. Il y a plus d'informations dans le lien ci-dessus, et c'est encore ici.

Et je suppose que nous devrions montrer notre _test la fonction fonctionne aussi, n'est-ce pas ? Nous déclarons simplement empty=something comme paramètre de notre fonction (ou à tout moment avant) :

    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!

Il convient de noter que cette évaluation est autonome - elle ne nécessite aucun test supplémentaire pour échouer. Quelques exemples supplémentaires :

    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?

Et donc finalement nous revenons à la question initiale :comment gérer les erreurs dans un $(command substitution) sous-shell ? La vérité est qu'il y a deux façons, mais aucune n'est directe. Le cœur du problème est le processus d'évaluation du shell - les extensions du shell (y compris $(command substitution) ) se produisent plus tôt dans le processus d'évaluation du shell que l'exécution actuelle de la commande shell, c'est-à-dire lorsque vos erreurs peuvent être détectées et piégées.

Le problème rencontré par l'op est qu'au moment où le shell actuel évalue les erreurs, le $(command substitution) le sous-shell a déjà été remplacé - il ne reste aucune erreur.

Quelles sont donc les deux voies ? Soit vous le faites explicitement dans le $(command substitution) sous-shell avec des tests comme vous le feriez sans lui, ou vous absorbez ses résultats dans une variable shell actuelle et testez sa valeur.

En relation:L'instruction PostgreSQL Select produit des erreurs dans datagrip mais pas pgAdmin4?

Méthode 1 :

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

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

    echo $?

126

Méthode 2 :

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

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

    echo $?

1

Cela
échouera quel que soit le nombre de variables déclarées par ligne :

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

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

Et notre valeur de retour reste constante :

    echo $?
1

MAINTENANT LE PIÈGE :

    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. Proxy TCP avec socat

  2. Substitution de commande :fractionnement sur une nouvelle ligne mais pas sur un espace ?

  3. Substitution de commande à l'aide de " ?

  4. Utilisation de l'exportation dans .bashrc ?

  5. Changer la police dans la commande Echo ?

16 exemples de commandes Echo sous Linux

Commande Echo sous Linux avec exemples

commande echo sous Linux :7 exemples pratiques

Utilisation de la commande Watch sous Linux

Utilisation de la commande tr sous Linux pour jouer avec des personnages

Comment insérer une nouvelle ligne dans l'e-mail à l'aide de la commande linux mail ?