Je to kvůli výběru při implementaci.
Spuštění stejného skriptu na Solaris s ksh93
vytváří jiné chování:
$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]
Problém spouští vnitřní kanál, bez něj smyčka opustí jakýkoli shell/OS:
$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$
cat
dostává signál SIGPIPE pod bash, ale shell stejně iteruje smyčku.
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) ---
Bash dokumentace uvádí:
Shell čeká na všechny příkazy v kanálu k ukončení před vrácením hodnoty.
Ksh dokumentace uvádí:
Každý příkaz, možná kromě posledního, je spuštěn jako samostatný proces; shell čeká na poslední příkaz ukončit.
POSIX uvádí:
Pokud potrubí není na pozadí (viz Asynchronní seznamy), shell bude čekat na poslední příkaz zadaný v kanálu k dokončení a může také čekat na všechny příkazy dokončit.
Tento problém mě trápí roky. Díky jilliagre za popostrčení správným směrem.
Trochu zopakuji otázku, na mém linuxovém boxu to skončí podle očekávání:
while true ; do echo "ok"; done | head
Ale když přidám dýmku, není ukončit podle očekávání:
while true ; do echo "ok" | cat; done | head
To mě roky frustrovalo. Zvážením odpovědi napsané od jilliagre jsem přišel s touto úžasnou opravou:
while true ; do echo "ok" | cat || exit; done | head
Q.E.D. ...
No, ne tak docela. Zde je něco trochu složitějšího:
i=0
while true; do
i=`expr $i + 1`
echo "$i" | grep '0$' || exit
done | head
Tohle nefunguje správně. Přidal jsem || exit
takže ví, jak předčasně ukončit, ale úplně první echo
neodpovídá grep
takže smyčka okamžitě skončí. V tomto případě vás opravdu nezajímá stav ukončení grep
. Moje řešení je přidat další cat
. Zde je tedy vytvořený skript nazvaný "desítky":
#!/bin/bash
i=0
while true; do
i=`expr $i + 1`
echo "$i" | grep '0$' | cat || exit
done
To se správně ukončí při spuštění jako tens | head
. Díky Bohu.