2015-12-20 1 views
3

J'essaie de comprendre pourquoi chaque fois que j'utilise function 2>&1 | tee -a $LOG tee crée un sous-shell dans la fonction qui ne peut pas être quitté par simple exit 1 (et si je n'utilise pas tee cela fonctionne très bien). Ci-dessous l'exemple:Dans bash, comment quitter un script à partir d'une fonction acheminée par tee?

#!/bin/bash 
LOG=/root/log.log 

function first() 
{ 
echo "Function 1 - I WANT to see this." 
exit 1 
} 

function second() 
{ 
echo "Function 2 - I DON'T WANT to see this." 
exit 1 
} 
first 2>&1 | tee -a $LOG 
second 2>&1 | tee -a $LOG 

Sortie:

[[email protected] ~]# ./1.sh 
Function 1 - I WANT to see this. 
Function 2 - I DON'T WANT to see this. 

So. si j'enlève la partie | tee -a $LOG, ça va fonctionner comme prévu (le script sera quitté dans la première fonction).

Pouvez-vous, s'il vous plaît, expliquer comment surmonter cela et quitter correctement la fonction tout en étant en mesure de té sortie?

+0

"crée un sous-shell en fonction" Eh bien, vous l'avez déjà dit. Pourquoi? Parce que toutes les commandes simples dans un pipeline sauf le dernier sont exécutées dans des sous-cases (le dernier dépend d'une option ou quelque chose - j'ai oublié). Pourquoi? Car. Pour savoir comment quitter correctement, vérifiez l'état de sortie (si 'pipefail' est défini) ou' PIPESTATUS' après le pipeline. – 4ae1e1

+0

Suivi sur commentaire ci-dessus: l'option que j'ai oublié est 'lastpipe'. – 4ae1e1

+0

Connexe: http://stackoverflow.com/q/9277827/951890 –

Répondre

3

Si vous créez un pipeline, la fonction est exécutée dans un sous-shell et si vous exit à partir d'un sous-shell, seul le sous-shell sera affecté, pas le shell parent.

printPid(){ echo $BASHPID; } 

printPid #some value 
printPid #same value 
printPid | tee #an implicit subshell -- different value 
(printPid) #an explicit subshell -- also a different value 

Si, au lieu de vous faire aFunction | tee:

aFunction > >(tee) 

il y aura le même essentiel, à l'exception aFunction ne fonctionnera pas dans un sous-shell, et sera donc en mesure d'influer sur l'environnement actuel (Définissez des variables, appelez exit, etc.).

+0

Merci, PSkocik, c'est vraiment la solution nécessaire! – LinenG

1

Vous pouvez dire bash à l'échec si quoi que ce soit dans le pipeline échoue avec set -e -o pipefail:

$ cat test.sh 
#!/bin/bash 
LOG=~/log.log 

set -e -o pipefail 

function first() 
{ 
echo "Function 1 - I WANT to see this." 
exit 1 
} 

function second() 
{ 
echo "Function 2 - I DON'T WANT to see this." 
exit 1 
} 
first 2>&1 | tee -a $LOG 
second 2>&1 | tee -a $LOG 
$ ./test.sh 
Function 1 - I WANT to see this. 
+0

Merci! J'aime aussi cette solution - élégante, mais fonctionne uniquement sur bash v3 et supérieur (apparemment). – LinenG

+0

bash v3 a été publié en 2004. Je pense que vous serez OK. –

2

Utilisez PIPESTATUS pour récupérer l'état de sortie de la première commande dans le pipeline.

first 2>&1 | tee -a $LOG; test ${PIPESTATUS[0]} -eq 0 || exit ${PIPESTATUS[0]} 
second 2>&1 | tee -a $LOG; test ${PIPESTATUS[0]} -eq 0 || exit ${PIPESTATUS[0]}