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 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 l'interpréter incorrectement, 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à une affectation simple ,, my_var=$(made_up_name)
quittera le script avec set -e
(ie errexit).
Est -E/-o errtrace
censé fonctionner comme le code ci-dessus? Ou, très probablement, je l'ai mal lu?
echo $( made up name )
par$( made up name )
produit le comportement souhaité. Je n'ai pas d'explication cependant.var=$( pipe )
et$( pipe )
exemples seraient représentent tous deux points d' extrémité du tuyau alorspipe > echo
ne serait pas. Ma page de manuel dit: "1. L'échec d'une commande individuelle dans un pipeline multi-commandes ne doit pas provoquer la fermeture du shell. Seule l'échec du pipeline lui-même doit être pris en compte."echo
renvoie toujours 0. Cela doit être pris en compte dans l'analyse ...Réponses:
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 shell proxy comme je l'ai fait avecsh <<-\CMD
.Ok, donc, comme je l'ai dit dans les commentaires ci-dessus, je ne connais pas spécifiquement les bash
set -E
, mais je sais que les shells compatibles POSIX fournissent un moyen simple de tester une valeur si vous le souhaitez:Au- dessus , vous verrez que si je
parameter expansion
tester${empty?} _test()
encorereturn
est un passe - comme il est témoigna dans le dernierecho
Cela se produit parce que la valeur a échoué tue le$( command substitution )
sous - shell qui le contient, mais sa coquille mère -_test
à ce moment - garde sur le camionnage. Etecho
peu importe - il est très heureux de servir seulement un test\newline; echo
n'est pas .Mais considérez ceci:
Parce que j'ai alimenté l'
_test()'s
entrée avec un paramètre pré-évalué dans leINIT here-document
maintenant, la_test()
fonction n'essaie même pas de fonctionner du tout. De plus, lesh
shell abandonne apparemment le fantôme etecho "this doesnt even print"
ne s'imprime même pas.Ce n'est probablement pas ce que vous voulez.
Cela se produit car l'
${var?}
expansion des paramètres de style est conçue pour quitter leshell
en cas de paramètre manquant, cela fonctionne comme ceci :Je ne vais pas copier / coller le document entier, mais si vous voulez un échec pour un
set but null
valeur, vous utilisez le formulaire:Avec le
:colon
comme ci-dessus. Si vous voulez qu'unenull
valeur réussisse, 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 série de
_test():
Cela fonctionne avec toutes sortes de tests rapides, mais ci-dessus, vous verrez que
_test()
, exécuté à partir du milieu despipeline
échecs, et en fait soncommand list
sous-shell contenant échoue complètement, car aucune des commandes de la fonction ne s'exécute ni la suivanteecho
, bien qu'il soit également démontré qu'il peut facilement être testé carecho "now it prints"
il s'imprime maintenant.Le diable est dans les détails, je suppose. Dans le cas ci-dessus, le shell qui sort n'est pas celui du script
_main | logic | pipeline
mais le( subshell in which we ${test?} ) ||
un petit bac à sable est donc nécessaire.Et ce n'est peut-être pas évident, mais si vous vouliez passer uniquement pour le cas contraire, ou uniquement pour les
set=
valeurs, c'est aussi assez simple:L'exemple ci-dessus tire parti des 4 formes de substitution de paramètres POSIX et de leurs diverses
:colon null
ounot null
des tests. Il y a plus d'informations dans le lien ci-dessus, et le voici à nouveau .Et je suppose que nous devrions aussi montrer notre
_test
travail fonctionnel, non? Nous déclarons simplementempty=something
comme paramètre de notre fonction (ou à tout moment à l'avance):Il convient de noter que cette évaluation est autonome - elle ne nécessite aucun test supplémentaire pour échouer. Quelques exemples supplémentaires:
Et enfin, nous revenons à la question initiale: comment gérer les erreurs dans un
$(command substitution)
sous-shell? La vérité est - 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 des commandes du shell - c'est à ce moment que 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)
sous shell a déjà été remplacé - aucune erreur ne subsiste.Alors, quelles sont les deux façons? Soit vous le faites explicitement dans le
$(command substitution)
sous shell avec des tests comme vous le feriez sans, soit vous absorbez ses résultats dans une variable shell actuelle et testez sa valeur.Méthode 1:
Méthode 2:
Cela échouera quel que soit le nombre de variables déclarées par ligne:
Et notre valeur de retour reste constante:
MAINTENANT LE PIÈGE:
la source
echo "abc" "${v1:?}"
cela ne semble pas s'exécuter (abc jamais imprimé). Et le shell retourne 1. Cela est vrai avec ou sans commande even ("${v1:?}"
directement sur le cli). Mais pour le script OP, tout ce qui était nécessaire pour déclencher le piège est de placer son affectation de variable contenant une substitution pour une commande inexistante seule sur une seule ligne. Sinon, le comportement de l'écho est de toujours retourner 0, à moins qu'il ne soit interrompu comme avec les tests que vous avez expliqué.v=$( madeup )
. Je ne vois pas ce qui est dangereux avec ça. C'est juste une affectation, la personne a mal orthographié la commande par exemplev="$(lss)"
. Il fait des erreurs. Oui, vous pouvez vérifier avec le statut d'erreur de la dernière commande $? - parce que c'est la commande sur la ligne (une affectation sans nom de commande) et rien d'autre - pas un argument à faire écho. De plus, ici, il est piégé par la fonction comme! = 0, donc vous obtenez deux fois des commentaires. Sinon, sûrement, comme vous l'expliquez, il existe une meilleure façon de le faire de manière ordonnée dans un cadre, mais OP avait une seule ligne: un écho plus sa substitution échouée. Il s'interrogeait sur l'écho.Dans votre script, c'est une exécution de commande (
echo $( made up name )
). Dans bash, les commandes sont délimitées par l'un ou l'autre ; ou avec une nouvelle ligne . Dans la commande$( made up name )
est considéré comme faisant partie de la commande. Même si cette partie échoue et revient avec une erreur, la commande entière s'exécute avec succès carecho
je n'en sais rien. Lorsque la commande revient avec 0, aucune interruption n'est déclenchée.Vous devez le mettre dans deux commandes, affectation et écho
la source
echo $var
n'échoue pas -$var
est étendu pour se vider avant même de pouvoirecho
y jeter un œil.Cela est dû à un bogue dans bash. Pendant l'étape de substitution de commande
made
s'exécute (ou ne parvient pas à être trouvé) dans un sous-shell, mais le sous-shell est "optimisé" de telle sorte qu'il n'utilise pas certains pièges du shell parent. Cela a été corrigé dans version 4.4.5 :Avec bash 4.4.5 ou supérieur, vous devriez voir la sortie suivante:
Le gestionnaire de trap a été appelé comme prévu, puis le sous-shell se termine. (
set -e
provoque uniquement la sortie du sous-shell, pas le parent, de sorte que le message "ne doit pas être atteint" doit en fait être atteint.)Une solution de contournement pour les anciennes versions consiste à forcer la création d'un sous-shell complet et non optimisé:
Les espaces supplémentaires sont nécessaires pour distinguer de l'expansion arithmétique.
la source