Bash: Comportement Head & Tail avec script bash

Supposons que je dispose du script suivant: –

test.sh

#!/bin/bash command1 #prints 5 lines command2 #prints 3 lines 

Je lance le script avec test.sh|head -n5

Que va-t-il se passer dans ce cas? Va-t-il exécuter les deux commandes? ou va-t-il s’arrêter après commande1? Et si je l’appelle avec -n1 ?

Contexte: Je pourrais poser une question très simple, mais j’ai remarqué quelque chose d’intéressant. Mon script (différent) traitait 7 000 fichiers et chaque fichier produisait une ligne de sortie. Il faut 7 minutes pour exécuter le script complètement, mais la commande head -n1 m’a donné une invite immédiatement, comme le script s’est terminé après le traitement du premier fichier uniquement.

Edit: Voici mon script

 for i in $(ls filepath);do echo "$i" # issue here python mySript "$i" > "/home/user/output/""$i"".out" fi done 

La suppression de l’ écho ci-dessus permet au script de s’exécuter complètement 7 minutes avec head -n1 , mais avec echo, il imprime simplement la première ligne, puis quitte.

C’est une question assez intéressante! Merci de l’avoir posté!

J’ai supposé que cela se produit lorsque la head sort après le traitement des premières lignes, de sorte que le signal SIGPIPE est envoyé à bash qui exécute le script lorsqu’il essaie de faire echo $x prochaine fois. J’ai utilisé le script de RedX pour prouver cette théorie:

 #!/usr/bin/bash rm x.log for((x=0;x<5;++x)); do echo $x echo $x>>x.log done 

Cela fonctionne, comme vous l’avez décrit! Utiliser t.sh|head -n 2 n’écrit que 2 lignes sur l’écran et sur x.log. Mais en piégeant SIGPIPE, ce comportement change …

 #!/usr/bin/bash trap "echo SIGPIPE>&2" PIPE rm x.log for((x=0;x<5;++x)); do echo $x echo $x>>x.log done 

Sortie:

 $ ./t.sh |head -n 2 0 1 ./t.sh: line 5: echo: write error: Broken pipe SIGPIPE ./t.sh: line 5: echo: write error: Broken pipe SIGPIPE ./t.sh: line 5: echo: write error: Broken pipe SIGPIPE 

L’erreur d’écriture se produit lorsque stdout est déjà fermé lorsque l’autre extrémité du canal est fermée. Et toute tentative d’écriture sur le canal fermé provoque un signal SIGPIPE, qui termine le programme par défaut (voir le man 7 signal ). Le fichier x.log contient maintenant 5 lignes.

Cela explique également pourquoi /bin/echo résolu le problème. Voir le script suivant:

 rm x.log for((x=0;x<5;++x)); do /bin/echo $x echo "Ret: $?">&2 echo $x>>x.log done 

Sortie:

 $ ./t.sh |head -n 2 0 Ret: 0 1 Ret: 0 Ret: 141 Ret: 141 Ret: 141 

Décimal 141 = hex 8D. Hex 80 signifie qu’un signal a été reçu, hexadécimal 0D est pour SIGPIPE. Donc, quand /bin/echo essayé d’écrire sur stdout, il a obtenu un SIGPIPE et il s’est arrêté (comme comportement par défaut) au lieu de lancer le script.

Belle trouvaille D’après mes tests, c’est exactement comme vous l’avez dit. Par exemple, j’ai ce script qui mange juste le processeur, pour nous permettre de le repérer en top :

 for i in `seq 10` do echo $i x=`seq 10000000` done 

En passant le script avec head -n1 nous voyons la commande revenir après la première ligne. C’est le comportement de la head : il a terminé son travail, il peut donc s’arrêter et vous rendre le contrôle.

Le script d’entrée doit continuer à fonctionner mais regardez ce qui se passe: lorsque la head revient, son pid n’existe plus. Donc, lorsque linux essaie d’envoyer la sortie du script au processus head, il ne trouve pas le processus, le script se bloque et s’arrête.

Essayons avec un script python:

 for i in xrange(10): print i range(10000000) 

Lorsque vous le lancez et que vous vous dirigez vers la tête, vous avez ceci:

 $ python -u test.py | head -n1 0 Traceback (most recent call last): File "test.py", line 2, in  print i IOError: [Errno 32] Broken pipe 

L’option -u indique à python de vider automatiquement stdin et stdout, comme le ferait bash. Donc, vous voyez que le programme s’arrête réellement avec une erreur.

C’est plus un commentaire qu’une réponse mais c’est trop gros pour un commentaire.

J’ai essayé le script suivant:

 #!/usr/bin/env bash rm -f "test_head.log" echo "1 line" echo "1 line" >> "test_head.log" echo "2 line" echo "2 line" >> "test_head.log" echo "3 line" echo "3 line" >> "test_head.log" echo "4 line" echo "4 line" >> "test_head.log" echo "5 line" echo "5 line" >> "test_head.log" echo "6 line" echo "6 line" >> "test_head.log" echo "7 line" echo "7 line" >> "test_head.log" echo "8 line" echo "8 line" >> "test_head.log" 

Ensuite, j’ai exécuté le script avec:

./test_head.sh | tête -n1

La sortie du chat est (à ma grande surprise):

1 ligne

Je n’ai aucune idée de ce qu’il se passe.

Après avoir lu le commentaire @ymonad, je l’ai essayé et remplacé echo par /bin/echo et cela a résolu le problème. J’espère qu’il pourra vous en dire plus sur ce comportement.