Sonlandırılan alt komutla bağlantı kurulurken neden bash while döngüsü çıkmıyor?


12

Aşağıdaki komut neden çıkmıyor? Çıkıştan ziyade, döngü süresiz olarak çalışır.

Bu davranışı daha karmaşık bir kurulum kullanarak keşfetmiş olsam da, komutun en basit şekli aşağıdakilere indirgeniyor.

Çıkmaz:

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

Yukarıda yazım hatası yok. Her bir '|' bir borudur. 'Çıkış 1' çalıştırılan ve çıkan başka bir işlem anlamına gelir.

Ben "çıkış 1" while döngüde bir SIGPIPE neden (okuyucu olmadan bir boruya yazmak) ve döngü kopmasını bekliyoruz. Ancak, döngü çalışmaya devam eder.

Komut neden durmuyor?


zsh normal olarak çıkar.
Braiam

Yanıtlar:


13

Bunun nedeni uygulamadaki bir seçimdir.

Aynı komut dosyasını Solaris üzerinde çalıştırmak ksh93farklı bir davranış oluşturur:

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

Sorunu tetikleyen şey, iç boru hattı, onsuz, kabuk / OS ne olursa olsun döngüden çıkar:

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

cat bash altında bir SIGPIPE sinyali alıyor ancak kabuk yine de döngüyü yineliyor.

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 belgeleri şunları belirtir:

Kabuk , bir değer döndürmeden önce boru hattındaki tüm komutların sonlanmasını bekler .

Ksh belgeleri şunları belirtir:

Muhtemelen sonuncu hariç her komut ayrı bir işlem olarak çalıştırılır; kabuk son komutun bitmesini bekler .

POSIX şunları belirtir:

Boru hattı arka planda değilse (bkz. Eşzamansız Listeler), kabuk , boru hattında belirtilen son komutun tamamlanmasını ve tüm komutların tamamlanmasını bekleyebilir .


Sorunu tetikleyen şey tam olarak iç boru hattı değil, yerleşik echoSIGPIPE'yi görmezden geliyor. Sorunu env echo, echo(asıl echoikili dosyayı kullanılmaya zorlamak için) yerine kullanarak da yeniden oluşturabilirsiniz . (Aynı zamanda çıkış karşılaştır { echo hi; echo $? >&2; } | exit 1ve { env echo hi; echo $? >&2; } | exit 1.)
Lucas Werkmeister

1

Bu sorun yıllardır beni rahatsız etti. Doğru yönde dürtmek için jilliagre sayesinde.

Soruyu biraz yeniden yazarken, linux kutumda, bu beklendiği gibi kesiliyor:

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

Ben pipo eklerseniz Ancak, yok değil beklendiği gibi çıkın:

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

Bu beni yıllarca hayal kırıklığına uğrattı. Jilliagre tarafından yazılan cevabı düşünerek, bu harika düzeltmeyi buldum:

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

QED ...

Pek iyi değil. İşte biraz daha karmaşık bir şey:

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

Bu doğru çalışmıyor. Ben || exiterken nasıl sonlandırılacağını bilir, ama ilk döngü echoeşleşmez grepböylece eşleşmiyor . Bu durumda, gerçekten çıkış durumuyla ilgilenmezsiniz grep. Benim geçici çözüm başka bir eklemektir cat. Yani, "onlarca" denilen bir senaryo:

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

Bu, çalıştırıldığında düzgün şekilde sonlanır tens | head. Tanrıya şükür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.