Bir borudan okurken 'sed q' neden farklı çalışıyor?


25

Aşağıdakileri içeren 'test' adlı bir test dosyası oluşturdum:

xxx
yyy
zzz

Komutu koştum:

(sed '/y/ q'; echo aaa; cat) < test

ve anladım:

xxx
yyy
aaa
zzz

Sonra koştum:

cat test | (sed '/y/ q'; echo aaa; cat)

ve var:

xxx
yyy
aaa

Soru

sed'y' ile bir çizgiyle karşılaşana kadar okur ve yazdırır, sonra durur. İlk durumda, ama ikincisinde değil, kedi geri kalanını okur ve yazdırır.

Birisi davranıştaki bu farkın arkasındaki olguyu açıklayabilir mi?

Ayrıca Ubuntu 16.04 ve Centos 6'da bu şekilde çalıştığını fark ettim ama Centos 7'de hiçbir komut 'zzz' basmıyor.


Benim tahminim cat(alt kabukta), ilk durumda dosya tanımlayıcısını yeniden kullanabileceği, çünkü stdin gerçek bir dosyaya bağlı. İkinci durumda stdin, bir dosyadan değil gerçek bir dosyadan geliyor. Ayrıca (sed '/y/ q'; echo aaa; cat) < <(cat test)yazdırmadığını unutmayın zzz.
Martin Nyolt

1
Daha basit bir örnek: (head -n1; head -n1) < testvecat test | (head -n1; head -n1)
Martin Nyolt 11

Yanıtlar:


22

Girdi dosyası aranabildiği zaman (normal dosyadan okumak gibi) veya aranamayan (bir borudan okumak gibi), sed(ve diğer standart programlar) farklı davranacaktır ( bu bağlantıdakiINPUT FILES bölümü okuyun ).

Dokümandan alıntı:

Standart bir yardımcı program aranabilir bir giriş dosyasını okuduğunda ve dosya sonuna ulaşmadan önce hatasız bir şekilde sona erdiğinde, yardımcı program açık dosya tanımındaki dosya ofsetinin yardımcı program tarafından işlenen son bayttan hemen sonra düzgün bir şekilde konumlandırılmasını sağlamalıdır.

Yani içinde:

(sed '/y/ q'; echo aaa; cat) < test

sedqEOF'ye ulaşmadan önce uit komutunu uyguladı , böylece zzzsatır başında dosya ofseti bıraktı , böylece catkalan satırları yazdırmaya devam edebildi (GNU sed bazı durumlarda POSIX uyumlu değil, aşağıya bakın).

Ve doktordan devam:

Aranamayan dosyalar için, o dosya için açık dosya açıklamasında dosyanın konumu belirtilmemiş

Bu durumda, davranış belirtilmemiş. Çoğu standart araç, sedgirdiyi mümkün olduğu kadar tüketecektir. Bu geçmesi okumak yyyhattı ve qhiçbir şey bırakılır böylece, ofset dosyayı kurtarmak olmadan uit cat.


GNU sedstandarda uygun değildir, sistemin stdio uygulamasına ve glibc versiyonuna bağlıdır:

$ (gsed '/y/ q'; echo aaa; cat) < test
xxx
yyy
aaa

Burada, Mac OSX 10.11.6, CEPH arka uçlu Openstack üzerinde çalıştırılan sanal makineler Centos 7.2 - glibc 2.17, Ubuntu 14.04 - glibc 2.19 elde edildi.

Bu sistemlerde, -ustandart davranışı elde etmek için seçeneği kullanabilirsiniz :

(gsed -u '/y/ q'; echo aaa; cat) </tmp/test

ve boru için:

$ cat test | (gsed -u '/y/ q'; echo aaa; cat)
xxx
yyy
aaa
zzz

bu korkunç verimsiz performansa yol açar, çünkü sedher seferinde bir byte okumak zorundadır. Aşağıdakilerden bir kısmı çıktı strace:

$ strace -fe read sh -c '{ sed -u "/y/q"; echo aaa; cat; } <test'
...
[pid  5248] read(3, "", 4096)           = 0
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "\n", 1)            = 1
xxx
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "\n", 1)            = 1
yyy
...

1
GNU için sedbu, sistemin stdio uygulamasına bağlıdır. GNU sistemlerinde (GNU libc ile), GNU stdio tarafından yönetilen dosyalar için geri aranacağı sedgibi uyumlu exit()olacaktır.
Stéphane Chazelas

@ StéphaneChazelas: Nasıl doğrulanır? Centos 7.2, Ubuntu 14.04 VM ile seduyumlu değil, benim manjaro dizüstü bilgisayarım var, hepsi aynı sed sürüm
4.2.2'ye

@ StéphaneChazelas: Kaputun altında bir şey olmuş gibi görünüyor. Sanal makinelerimde, strace -f sh -c '{ sed "/y/q"; echo aaa; cat; } <test'hiçbir işlem lseek()yapılmadığını ve manjaro'mda lseek()daha önce çağrıldığını gösterin exit_group().
cuonglm

Sanırım bu GNU libc versiyonuna bağlı. Bir main() { char buf[999]; gets(buf); }'programla test edebilirsiniz .
Stéphane Chazelas

1
@ StéphaneChazelas: Onaylandı. Her iki VM'mde de 2,17 ve 2,19 var, manjaroda ise 2,23. Bu bir glibc hatası mıdır? Glibc versiyonları arasındaki değişim hakkında herhangi bir bilginiz var mı
cuonglm
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.