GNU/Linux >> Tutoriels Linux >  >> Linux

pourquoi une boucle bash while ne se termine-t-elle pas lorsqu'elle est dirigée vers une sous-commande terminée?

Elle est due à un choix d'implémentation.

Exécution du même script sur Solaris avec ksh93 produit un comportement différent :

$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]

Ce qui déclenche le problème, c'est le pipeline interne, sans lui, la boucle se termine quel que soit le shell/OS :

$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$

cat reçoit un signal SIGPIPE sous bash mais le shell parcourt quand même la boucle.

Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

Frapper la documentation indique :

Le shell attend toutes les commandes dans le pipeline pour se terminer avant de renvoyer une valeur.

Ksh la documentation indique :

Chaque commande, sauf peut-être la dernière, est exécutée comme un processus séparé; le shell attend la dernière commande pour terminer.

POSIX indique :

Si le pipeline n'est pas en arrière-plan (voir Listes asynchrones), le shell attendra la dernière commande spécifié dans le pipeline pour se terminer, et peut également attendre toutes les commandes à terminer.


Ce problème m'a embêté pendant des années. Merci à jilliagre pour le coup de pouce dans la bonne direction.

En reformulant un peu la question, sur ma machine Linux, cela se termine comme prévu :

while true ; do echo "ok"; done | head

Mais si j'ajoute un tuyau, il ne le fait pas quitter comme prévu :

while true ; do echo "ok" | cat; done | head

Cela m'a frustré pendant des années. En considérant la réponse écrite par jilliagre, j'ai trouvé cette merveilleuse solution :

while true ; do echo "ok" | cat || exit; done | head

Q.E.D. ...

Eh bien, pas tout à fait. Voici quelque chose d'un peu plus compliqué :

i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' || exit
done | head

Cela ne fonctionne pas correctement. J'ai ajouté le || exit il sait donc se terminer plus tôt, mais le tout premier echo ne correspond pas au grep donc la boucle s'arrête tout de suite. Dans ce cas, vous n'êtes vraiment pas intéressé par le statut de sortie du grep . Ma solution consiste à ajouter un autre cat . Donc, voici un script artificiel appelé "dizaines":

#!/bin/bash
i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' | cat || exit
done

Cela se termine correctement lorsqu'il est exécuté en tant que tens | head . Dieu merci.


Linux
  1. Script Linux :3 tutoriels pour les boucles while dans Bash

  2. Pourquoi l'invite Bash est-elle boguée lorsque je parcours l'historique ? ?

  3. Code de sortie par défaut lorsque le processus est terminé ?

  4. Exemples de boucle Bash For et de boucle While

  5. Vous tenir au courant - Exemples de boucle Bash For, While, Until

Bash break :comment sortir d'une boucle

Bash pendant la boucle

Bash jusqu'à la boucle

Bash Scripting - Boucle While And Until expliquée avec des exemples

Bash :Pourquoi le script parent ne se termine-t-il pas sur SIGINT lorsque le script enfant piège SIGINT ?

Pourquoi vfork() est-il destiné à être utilisé lorsque le processus enfant appelle exec() ou exit() immédiatement après la création ?