J'ai deux processus foo
et bar
, connecté avec un tuyau:
$ foo | bar
bar
quitte toujours 0; Je suis intéressé par le code de sortie de foo
. Y a-t-il un moyen d'y arriver?
J'ai deux processus foo
et bar
, connecté avec un tuyau:
$ foo | bar
bar
quitte toujours 0; Je suis intéressé par le code de sortie de foo
. Y a-t-il un moyen d'y arriver?
Réponses:
Si vous utilisez bash
, vous pouvez utiliser la PIPESTATUS
variable de tableau pour obtenir le statut de sortie de chaque élément du pipeline.
$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0
Si vous utilisez zsh
, ils s'appellent tableau pipestatus
(la casse compte!) Et les indices de tableau commencent à un:
$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0
Pour les combiner dans une fonction de manière à ne pas perdre les valeurs:
$ false | true
$ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
$ echo $retval_bash $retval_zsh $retval_final
1 0
Exécutez ce qui précède dans bash
ou zsh
et vous obtiendrez les mêmes résultats. un seul retval_bash
et retval_zsh
sera réglé. L'autre sera vide. Cela permettrait à une fonction de se terminer par return $retval_bash $retval_zsh
(notez le manque de guillemets!).
pipestatus
en zsh. Malheureusement, d'autres coquilles n'ont pas cette fonctionnalité.
echo "$pipestatus[1]" "$pipestatus[2]"
.
if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
Il y a 3 façons courantes de le faire:
La première consiste à définir l’ pipefail
option ( ksh
, zsh
ou bash
). C’est le plus simple. En général, l’état $?
de sortie est défini sur le code de sortie du dernier programme à quitter non nul (ou zéro si tous les éléments sont sortis avec succès).
$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1
Bash a également une variable de tableau appelée $PIPESTATUS
( $pipestatus
in zsh
) qui contient l’état de sortie de tous les programmes du dernier pipeline.
$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1
Vous pouvez utiliser l'exemple de la 3ème commande pour obtenir la valeur spécifique dans le pipeline dont vous avez besoin.
C'est la plus lourde des solutions. Exécutez chaque commande séparément et capturez le statut
$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1
ksh
, mais d'un simple coup d'œil à sa page de manuel, elle ne prend pas en charge $PIPESTATUS
ou quelque chose de similaire. Il supporte cependant l' pipefail
option.
LOG=$(failed_command | successful_command)
Cette solution fonctionne sans utiliser de fonctionnalités spécifiques à bash ni de fichiers temporaires. Bonus: au final, le statut de sortie est en fait un statut de sortie et non une chaîne dans un fichier.
Situation:
someprog | filter
vous voulez le statut de sortie someprog
et la sortie filter
.
Voici ma solution:
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
le résultat de cette construction est stdout from filter
as stdout de la construction et status de someprog
exit from exit statut de la construction.
cette construction fonctionne également avec un simple regroupement de commandes {...}
au lieu de sous-shell (...)
. Les sous-coques ont des implications, entre autres un coût de performance, dont nous n’avons pas besoin ici. lisez le manuel de Bash pour plus de détails: https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1
Malheureusement, la grammaire bash nécessite des espaces et des points-virgules pour les accolades afin que la construction devienne beaucoup plus spacieuse.
Pour la suite de ce texte, je vais utiliser la variante du sous-shell.
Exemple someprog
et filter
:
someprog() {
echo "line1"
echo "line2"
echo "line3"
return 42
}
filter() {
while read line; do
echo "filtered $line"
done
}
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
echo $?
Exemple de sortie:
filtered line1
filtered line2
filtered line3
42
Remarque: le processus enfant hérite des descripteurs de fichier ouverts du parent. Cela signifie someprog
qu’il héritera des descripteurs de fichier ouverts 3 et 4. Si l’ someprog
écriture dans le descripteur de fichier 3 devient le statut de sortie. Le statut de sortie réel sera ignoré car il read
ne lit qu'une fois.
Si vous craignez d' someprog
écrire dans le descripteur de fichier 3 ou 4, il est préférable de fermer les descripteurs de fichier avant d'appeler someprog
.
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
L' exec 3>&- 4>&-
avant someprog
ferme le descripteur de fichier avant de l'exécuter, someprog
il someprog
n'existe donc tout simplement pas pour ces descripteurs de fichier.
Cela peut aussi être écrit comme ceci: someprog 3>&- 4>&-
Explication pas à pas de la construction:
( ( ( ( someprog; #part6
echo $? >&3 #part5
) | filter >&4 #part4
) 3>&1 #part3
) | (read xs; exit $xs) #part2
) 4>&1 #part1
De bas en haut:
#part3
) et de droite ( #part2
) sont exécutées. exit $xs
est également la dernière commande du tube et cela signifie que la chaîne de stdin sera le statut de sortie de la construction entière.#part2
devenir le statut de sortie de la construction entière.#part5
et #part6
) et à droite ( filter >&4
) sont exécutées. La sortie de filter
est redirigée vers le descripteur de fichier 4. Dans #part1
le descripteur de fichier, 4 a été redirigé vers la sortie standard. Cela signifie que la sortie de filter
est la sortie standard de la construction entière.#part6
est imprimé dans le descripteur de fichier 3. Dans #part3
le descripteur de fichier 3 a été redirigé vers #part2
. Cela signifie que le statut de sortie de #part6
sera le statut de sortie final de la construction entière.someprog
est exécuté. Le statut de sortie est pris en compte #part5
. La sortie standard est prise par le tuyau #part4
et transmise à filter
. La sortie de filter
aboutira à son tour à stdout comme expliqué dans#part4
(read; exit $REPLY)
(exec 3>&- 4>&-; someprog)
simplifie à someprog 3>&- 4>&-
.
{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
Bien que pas exactement ce que vous avez demandé, vous pouvez utiliser
#!/bin/bash -o pipefail
afin que vos tuyaux renvoient le dernier retour non nul.
pourrait être un peu moins codant
Edit: Exemple
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1
set -o pipefail
l'intérieur du script devrait être plus robuste, par exemple au cas où quelqu'un l'exécuterait via bash foo.sh
.
-o pipefail
n'est pas dans POSIX.
#!/bin/bash -o pipefail
. L'erreur est:/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
#!
lignes au-delà de la première, de sorte que cela devient /bin/bash
-o pipefail
/tmp/ff
, au lieu de l' analyse nécessaire /bin/bash
-o
pipefail
/tmp/ff
- getopt
(ou similaire), l'utilisation de optarg
, l'élément suivant dans ARGV
l'argument to -o
, donc ça échoue. Si vous deviez créer un wrapper (par exemple, bash-pf
cela vient de se produire exec /bin/bash -o pipefail "$@"
, et le mettrait en #!
ligne, cela fonctionnerait. Voir aussi: en.wikipedia.org/wiki/Shebang_%28Unix%29
Ce que je fais lorsque cela est possible est d'alimenter le code de sortie foo
en bar
. Par exemple, si je sais que cela foo
ne produit jamais une ligne comportant uniquement des chiffres, je peux simplement ajouter le code de sortie:
{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'
Ou si je sais que la sortie de foo
ne contient jamais une ligne avec juste .
:
{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'
Cela peut toujours être fait s'il existe un moyen de bar
travailler sur toutes les lignes sauf la dernière et de passer à la dernière ligne comme code de sortie.
S'il bar
s'agit d'un pipeline complexe dont vous n'avez pas besoin en sortie, vous pouvez en contourner une partie en imprimant le code de sortie sur un descripteur de fichier différent.
exit_codes=$({ { foo; echo foo:"$?" >&3; } |
{ bar >/dev/null; echo bar:"$?" >&3; }
} 3>&1)
Après cela $exit_codes
est généralement foo:X bar:Y
, mais cela pourrait être bar:Y foo:X
si vous bar
quittez avant de lire toutes ses entrées ou si vous êtes malchanceux. Je pense aux tuyaux écrit jusqu'à 512 octets sont atomiques sur tous unix, de sorte que les foo:$?
et bar:$?
parties ne seront pas entremêlées tant que les chaînes de balise sont sous 507 octets.
Si vous devez capturer la sortie bar
, cela devient difficile. Vous pouvez combiner les techniques ci-dessus en faisant en sorte que la sortie de bar
ne jamais contenir une ligne qui ressemble à une indication de code de sortie, mais cela devient difficile.
output=$(echo;
{ { foo; echo foo:"$?" >&3; } |
{ bar | sed 's/^/^/'; echo bar:"$?" >&3; }
} 3>&1)
nl='
'
foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*}
bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*}
output=$(printf %s "$output" | sed -n 's/^\^//p')
Et, bien sûr, il existe la possibilité simple d’ utiliser un fichier temporaire pour stocker le statut. Simple, mais pas si simple en production:
/tmp
le seul endroit où un script est sûr de pouvoir écrire des fichiers. Utilisez mktemp
, ce qui n’est pas POSIX mais disponible sur tous les ordinateurs sérieux de nos jours.foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")
À partir du pipeline:
foo | bar | baz
Voici une solution générale utilisant uniquement un shell POSIX et aucun fichier temporaire:
exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
(bar || echo "1:$?" >&3) |
(baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-
$error_statuses
contient les codes d'état de tous les processus en échec, dans un ordre aléatoire, avec des index pour indiquer quelle commande a émis chaque état.
# if "bar" failed, output its status:
echo "$error_statuses" | grep '1:' | cut -d: -f2
# test if all commands succeeded:
test -z "$error_statuses"
# test if the last command succeeded:
! echo "$error_statuses" | grep '2:' >/dev/null
Notez les citations autour $error_statuses
de mes tests; sans eux, il grep
est impossible de faire la différence car les nouvelles lignes sont contraintes aux espaces.
Je voulais donc apporter une réponse semblable à celle de Lesmana, mais je pense que la mienne est peut-être une solution un peu plus simple et légèrement plus avantageuse de pure Bourne-shell:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
Je pense que ceci est mieux expliqué de l'intérieur - Command1 exécutera et imprimera sa sortie standard sur stdout (descripteur de fichier 1), puis une fois terminé, printf exécutera et imprimera le code de sortie de command1 sur sa sortie standard, mais ce dernier sera redirigé vers descripteur de fichier 3.
Pendant que commande1 est en cours d'exécution, son stdout est redirigé vers commande2 (la sortie de printf ne le fait jamais sur commande2 car nous l'envoyons dans le descripteur de fichier 3 au lieu de 1, ce qui est lu par le canal). Ensuite, nous redirigeons la sortie de command2 vers le descripteur de fichier 4, de sorte qu'il reste également en dehors du descripteur de fichier 1 - car nous voulons que le descripteur de fichier 1 soit libre un peu plus tard, car nous ramènerons la sortie de printf sur le descripteur de fichier 3 dans le descripteur de fichier. 1 - parce que c'est ce que la substitution de commande (les backticks) va capturer et c'est ce qui sera placé dans la variable.
La magie finale est que exec 4>&1
nous avons d' abord créé une commande distincte: elle ouvre le descripteur de fichier 4 en tant que copie de la sortie standard du shell externe. La substitution de commande capturera tout ce qui est écrit en sortie standard du point de vue des commandes qu'il contient - mais, comme la sortie de command2 est dirigée vers le descripteur de fichier 4 en ce qui concerne la substitution de commande, la substitution de commande ne le capture pas - cependant, une fois qu'il est "sorti" de la substitution de commande, il reste dans le descripteur de fichier général du script 1.
(Il exec 4>&1
doit s'agir d'une commande séparée, car de nombreux shells courants ne l'aiment pas lorsque vous essayez d'écrire dans un descripteur de fichier à l'intérieur d'une substitution de commande, qui est ouverte dans la commande "external" qui utilise la substitution. moyen portable le plus simple de le faire.)
Vous pouvez le regarder de manière moins technique et plus ludique, comme si les sorties des commandes se superposaient: command1 passe à command2, puis la sortie de printf saute par-dessus la commande 2 de sorte que commande2 ne l'attrape pas, puis la sortie de la commande 2 saute par-dessus la substitution de commande juste au moment où printf atterrit juste à temps pour être capturé par la substitution, de sorte qu'il se retrouve dans la variable, et la sortie de la commande2 continue à être joyeusement écrite sur la sortie standard, comme dans un tuyau normal.
De plus, si j'ai bien compris, il $?
contiendra toujours le code de retour de la deuxième commande dans le tube, car les affectations de variables, les substitutions de commandes et les commandes composées sont toutes transparentes pour le code de retour de la commande qu'elles contiennent. command2 devrait être propagé - ceci, et ne pas avoir à définir une fonction supplémentaire, est la raison pour laquelle je pense que cela pourrait être une solution légèrement meilleure que celle proposée par lesmana.
Selon les mises en garde lesmana, il est possible que command1 finisse par utiliser les descripteurs de fichier 3 ou 4, aussi, pour être plus robuste, vous feriez:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Notez que j’utilise des commandes composées dans mon exemple, mais des sous-shell (utiliser ( )
au lieu de { }
fonctionnera également, même s’il est peut-être moins efficace.)
Les commandes héritent des descripteurs de fichier du processus qui les lance, de sorte que la deuxième ligne entière hérite du descripteur de fichier quatre et que la commande composée suivie de 3>&1
hérite du descripteur de fichier trois. Ainsi, le groupe 4>&-
s'assure que la commande composée interne n'héritera pas du descripteur de fichier quatre et 3>&-
n'héritera pas du descripteur de fichier trois. Ainsi, commande1 obtient un environnement plus «propre», plus standard. Vous pouvez également déplacer l’intérieur à 4>&-
côté du 3>&-
, mais j’imagine pourquoi ne pas simplement en limiter le plus possible la portée.
Je ne suis pas sûr de la fréquence à laquelle les choses utilisent les descripteurs de fichier trois et quatre directement. Je pense que la plupart du temps, les programmes utilisent des appels système qui renvoient des descripteurs de fichier non utilisés pour le moment, mais que le code écrit parfois directement dans le descripteur de fichier 3. devinez (je pourrais imaginer un programme vérifiant un descripteur de fichier pour voir s'il est ouvert, et l'utiliser si c'est le cas, ou se comporter différemment si ce n'est pas le cas). Donc, ce dernier est probablement préférable de garder à l'esprit et à utiliser pour les cas à usage général.
-bash: 3: Bad file descriptor
.
Si vous avez le paquet moreutils installé, vous pouvez utiliser l’ utilitaire faux - tuyau qui fait exactement ce que vous avez demandé.
La solution de lesmana ci-dessus peut également être réalisée sans la surcharge de démarrer des sous-processus imbriqués en utilisant à la { .. }
place (en se rappelant que cette forme de commandes groupées doit toujours se terminer par des points-virgules). Quelque chose comme ça:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
J'ai vérifié cette construction avec la version de tableau de bord 0.5.5 et les versions 3.2.25 et 4.2.42 de Bash. Par conséquent, même si certains shells ne prennent pas en charge le { .. }
groupement, ils sont toujours compatibles POSIX.
set -o pipefail
in ksh ou un nombre quelconque de wait
commandes saupoudrées . Je pense que cela peut, au moins en partie, être un problème d’analyse syntaxique pour ksh, comme si je m’en tenais à l’utilisation de subshells, alors cela fonctionnerait bien, mais même avec un if
choix de la variante de sous-shell pour ksh mais en laissant les commandes composées pour d’autres, cela échoue .
C'est portable, c'est-à-dire qu'il fonctionne avec n'importe quel shell compatible POSIX, ne nécessite pas que le répertoire actuel soit accessible en écriture et permet à plusieurs scripts utilisant la même astuce de s'exécuter simultanément.
(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))
Edit: voici une version plus forte suite aux commentaires de Gilles:
(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))
Edit2: et voici une variante légèrement plus légère suite au commentaire de dbiousjim:
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
. @Johan: Je suis d'accord que c'est plus facile avec Bash, mais dans certains contextes, savoir comment éviter Bash en vaut la peine.
Ce qui suit est considéré comme un complément à la réponse de @Patrik, au cas où vous ne pourriez pas utiliser l’une des solutions courantes.
Cette réponse suppose que:
$PIPESTATUS
ni deset -o pipefail
Hypothèses supplémentaires. Vous pouvez vous débarrasser de tout, mais cette recette est trop lourde, elle n'est donc pas couverte ici:
- Tout ce que vous voulez savoir, c'est que toutes les commandes du PIPE ont le code de sortie 0.
- Vous n'avez pas besoin d'informations supplémentaires sur la bande latérale.
- Votre shell attend que toutes les commandes de canal soient renvoyées.
Avant:, foo | bar | baz
cependant, cela ne retourne que le code de sortie de la dernière commande ( baz
)
Wanted: $?
ne doit pas être 0
(true), si l'une des commandes du canal a échoué
Après:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo || echo $? >&9; } |
{ bar || echo $? >&9; } |
{ baz || echo $? >&9; }
#wait
! read TMPRESULTS <&8
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
# $? now is 0 only if all commands had exit code 0
A expliqué:
mktemp
. Cela crée généralement immédiatement un fichier dans/tmp
wait
est nécessaire pour ksh
, car ksh
sinon n'attend pas que toutes les commandes de canal soient terminées. Toutefois, veuillez noter qu'il existe des effets indésirables si certaines tâches d'arrière-plan sont présentes. Je l'ai donc commenté par défaut. Si l'attente ne fait pas mal, vous pouvez le commenter.read
retours false
, donc true
indique une erreurCeci peut être utilisé comme remplacement de plugin pour une seule commande et n'a besoin que de ce qui suit:
/proc/fd/N
Bogues:
Ce script a un bug dans le cas où l' /tmp
espace est insuffisant. Si vous avez besoin de protection contre ce cas artificiel, aussi, vous pouvez le faire comme suit, mais cela présente l'inconvénient, que le nombre d' 0
en 000
fonction du nombre de commandes dans le tuyau, il est donc un peu plus compliqué:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo; printf "%1s" "$?" >&9; } |
{ bar; printf "%1s" "$?" >&9; } |
{ baz; printf "%1s" "$?" >&9; }
#wait
read TMPRESULTS <&8
[ 000 = "$TMPRESULTS" ]
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
Notes de portabilité:
ksh
et des coquilles similaires qui n'attendent que la dernière commande de tuyau ont besoin du non wait
commenté
Le dernier exemple utilise printf "%1s" "$?"
au lieu de echo -n "$?"
parce que c'est plus portable. Toutes les plateformes n'interprètent pas -n
correctement.
printf "$?"
le ferait aussi bien, cependant printf "%1s"
attrape certains cas au cas où vous exécutez le script sur une plate-forme vraiment cassée. (Lire: si vous programmez paranoia_mode=extreme
.)
Les formats FD 8 et FD 9 peuvent être supérieurs sur les plates-formes prenant en charge plusieurs chiffres. AFAIR, un shell conforme à POSIX n'a besoin que de prendre en charge les chiffres uniques.
A été testé avec Debian 8.2 sh
, bash
, ksh
, ash
, sash
et mêmecsh
Avec un peu de précaution, cela devrait fonctionner:
foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
Le bloc 'if' suivant ne sera exécuté que si 'commande' a réussi:
if command; then
# ...
fi
Plus précisément, vous pouvez exécuter quelque chose comme ceci:
haconf_out=/path/to/some/temporary/file
if haconf -makerw > "$haconf_out" 2>&1; then
grep -iq "Cluster already writable" "$haconf_out"
# ...
fi
Qui va exécuter haconf -makerw
et stocker son stdout et stderr dans "$ haconf_out". Si la valeur renvoyée haconf
est true, le bloc 'if' sera exécuté et grep
indiquera "$ haconf_out", en essayant de le faire correspondre à "Le cluster est déjà accessible en écriture".
Notez que les tuyaux se nettoient automatiquement; avec la redirection, vous devrez prendre soin de supprimer "$ haconf_out" lorsque vous aurez terminé.
Pas aussi élégant que pipefail
, mais une alternative légitime si cette fonctionnalité n’est pas à la portée de la main.
Alternate example for @lesmana solution, possibly simplified.
Provides logging to file if desired.
=====
$ cat z.sh
TEE="cat"
#TEE="tee z.log"
#TEE="tee -a z.log"
exec 8>&- 9>&-
{
{
{
{ #BEGIN - add code below this line and before #END
./zz.sh
echo ${?} 1>&8 # use exactly 1x prior to #END
#END
} 2>&1 | ${TEE} 1>&9
} 8>&1
} | exit $(read; printf "${REPLY}")
} 9>&1
exit ${?}
$ cat zz.sh
echo "my script code..."
exit 42
$ ./z.sh; echo "status=${?}"
my script code...
status=42
$
(Avec bash au moins) combiné avec set -e
on peut utiliser un sous-shell pour émuler explicitement pipefail et sortir en cas d'erreur
set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program
Donc si foo
échoue pour une raison quelconque, le reste du programme ne sera pas exécuté et le script se terminera avec le code d'erreur correspondant. (Cela suppose que soit foo
imprimée sa propre erreur, ce qui est suffisant pour comprendre la raison de l'échec)
EDIT : Cette réponse est fausse, mais intéressante, alors je la laisserai pour référence future.
!
à la commande inverse le code de retour.
http://tldp.org/LDP/abs/html/exit-status.html
# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command # bash: bogus_command: command not found
echo $? # 127
! ls | bogus_command # bash: bogus_command: command not found
echo $? # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
ls
- pas inverser le code de sortie debogus_command