pipe, {list; } yalnızca bazı programlarla çalışır


13

Öngörülemeyen bu tür davranışlar için ileri düzey kullanıcılar tarafından yapılan açıklamalara ihtiyacınız var:

ps -eF | { head -n 1;grep worker; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root       441     2  0     0     0   2 paź15 ?       00:00:00 [kworker/2:1H]

oysa her şey yolunda görünüyor

ls -la / | { head -n 1;grep sbin; }

yalnızca head

... Ben de düşündüm stdout 2>&1ve benim için işe yaramıyor garip, herhangi bir açıklama ya da nasıl ele?


1
Sonuncusu her şeyi yazdırmalıdır. headVe greporada hiçbir şey.
jordanm

Evet haklısın. Ama bunun yerine, ps -eF neden ls -la / değil iken çalışır?
ast

Yanıtlar:


9

Kullanarak bazı araştırmalar yaptım straceve boru hattının sol tarafındaki programın terminale yazdığı gibi görünüyor. Ne zaman lskomut yürütülür tek tüm verileri yazar write(). Bu, headtüm standartların tüketilmesine neden olur .

Öte yandan psverileri toplu olarak yazar, bu yüzden sadece ilk kişi write()tarafından tüketilir headve sonra var olur. Daha sonra yapılan çağrılar write()yeni ortaya çıkan grepsürece gidecektir .

Bu, çalıştığınız işlem grepilkinde gerçekleşmezse işe yaramayacağı anlamına gelir write(), çünkü greptüm verileri göremez (sadece ilk eksi veri eksi bile daha az görür).

İşte sistemimde pid 1 için grep denemeye bir örnek:

$ ps -eF | { head -n2; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | grep '/lib/systemd/systemd$'
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | { head -n1; grep '/lib/systemd/systemd$'; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD

Sizin ps -eFörnek sadece tesadüfen çalışır.


büyük ve kapsamlı expalnation çok teşekkürler
ast

1
Aslında daha çok bir yarış durumu. Sadece birden fazla write()çağrı yapmak daha yavaş . Çağrısını headgerçekleştirmek için yavaş olsaydı read()(boru arabelleği içindeki tüm verilere sahip olacak şekilde), hem lsve hem de aynı davranışı sergilerdi ps.
Patrick

6

Bu glibc'de tamponlamadan kaynaklanır. Halinde lsçıkışı, bir iç tampon ve bu şekilde sadece geçirilir head. Çünkü, ps -eFçıktı daha büyüktür ve bu yüzden headbittiğinde, aşağıdakilerin grepkalan kısımları (ancak tamamı değil) elde edilir ps.

Borunun arabelleğini kaldırarak ondan kurtulabilirsiniz - örneğin sed -u(GNU uzantısı olmadığından emin değilim):

$ ls -al / | sed -u "#" | { head -n 1; grep bin; }
total 76
drwxr-xr-x   2 root root  4096 Oct  2 21:52 bin
drwxr-xr-x   2 root root  8192 Oct  3 01:54 sbin

4

Neler oluyor head -n 11 satırdan fazla okur. En iyi verim için, kafa bayt yığınlarını okur, böylece bir seferde 1024 bayt okuyabilir ve sonra ilk satır sonu için bu baytlara bakabilir. Satır sonu, bu 1024 baytın ortasında meydana gelebileceğinden, verilerin geri kalanı kaybolur. Boruya geri konulamaz. Böylece yürüten bir sonraki işlem sadece bayt 1025 ve daha fazla olur.

İlk komutunuz başarılı olur çünkü kworkerişlem headokuyan ilk öbekten sonra gelir .

Bunun çalışması headiçin bir seferde 1 karakter okumak zorunda kalacaksınız. Ama bu son derece yavaş, öyle değil.
Böyle bir şeyi verimli bir şekilde yapmanın tek yolu, hem "kafa" hem de "grep" i tek bir işlem yapmaktır.

İşte bunu yapmanın 2 yolu:

echo -e '1\n2\n3\n4\n5' | perl -ne 'print if $i++ == 0 || /4/'

veya

echo -e '1\n2\n3\n4\n5' | awk '{if (NR == 1 || /4/) print }'

Çok daha fazlası var ...


evet Bu görevi yerine getirmek için 'awk' yöntemini biliyordum, ancak davranışın neden {list ile bu kadar tahmin edilemez olduğunu merak ediyordum; }. Nasıl çalıştığını açıklamak için teşekkürler. Yukarıdaki yanıtlardan çok etkilendim
ast

2

Yalnızca ilk veya iki satırı istiyorsanız, aşağıdaki hile türü çalışır ve çıktı akışını okumak için iki farklı komut kullanmanın neden olduğu arabelleğe alma sorunlarını önler:

$ ps -eF   | { IFS= read -r x ; echo "$x" ; grep worker; }
$ ls -la / | { IFS= read -r x ; echo "$x" ; grep sbin; }

readKabuk dahili ve böylece kullanarak, sadece çıkış bir satır girdi bütün bir tampon tüketmez readaşağıdaki komutu için yaprakları çıkış her şey.

İki farklı komut kullanan örnekleriniz tarafından gösterilen arabelleğe alma sorunlarını vurgulamak istiyorsanız sleep, zamanlama sorunlarını ortadan kaldırmak için bunlara a ekleyin ve sağdaki komutlardan herhangi birini okumadan önce soldaki komutun tüm çıktısını oluşturmasına izin verin o:

$ ps -eF   | { sleep 5 ; head -n 1 ; grep worker; }
$ ls -la / | { sleep 5 ; head -n 1 ; grep sbin; }

Şimdi, yukarıdaki örneklerin her ikisi de aynı şekilde başarısız olur - headsadece bir satırı üretmek için çıktının tüm bir tamponunu okur ve bu tampon aşağıdakiler için mevcut değildir grep.

Hangi satırların eksik olduğunu söyleyebilmek için çıktı satırlarını numaralandıran bazı örnekler kullanarak arabelleğe alma sorununu daha net görebilirsiniz:

$ ps -eF          | cat -n | { sleep 5 ; head -n 1 ; head ; }
$ ls -la /usr/bin | cat -n | { sleep 5 ; head -n 1 ; head ; }

Arabelleğe alma sorununu görmenin basit bir yolu, seqbir sayı listesi oluşturan kullanmaktır . Hangi sayıların kaybolacağını kolayca söyleyebiliriz:

$ seq 1 100000    | { sleep 5 ; head -n 1 ; head ; }
1

1861
1862
1863
1864
1865
1866
1867
1868
1869

İlk satırı okumak ve yankılamak için kabuğu kullanan hile çözümüm, uyku gecikmesi eklendiğinde bile doğru çalışıyor:

$ seq 1 100000 | { sleep 5 ; IFS= read -r x ; echo "$x" ; head ; }
1
2
3
4
5
6
7
8
9
10
11

Aşağıda, her seferinde beş satırını üretmek için çıktının tüm bir arabelleğini headnasıl headtükettiğini gösteren tamponlama sorunlarını gösteren tam bir örnek verilmiştir . Bu tüketilen arabellek, sıradaki bir sonraki headkomut için kullanılamaz :

$ seq 1 100000 | { sleep 5 ; head -5 ; head -5 ; head -5 ; head -5 ; }
1
2
3
4
5

1861
1862
1863
1864
499
3500
3501
3502
3503
7
5138
5139
5140
5141

Sayısına bakıldığında 1861yukarıda biz tampon boyutu tarafından kullanılan hesaplayabilir headsayarak seqçıkışı 1için 1860:

$ seq 1 1860 | wc -c
8193

headKendi çıktısının sadece birkaç satırını üretmek için bile, bir seferde tam bir 8KB (8 * 1024 bayt) boru çıkışı okuyarak tamponlama yaptığını görüyoruz .

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.