kafa ekstra karakterler yiyor


15

Aşağıdaki kabuk komutunun giriş akışının yalnızca tek satırlarını yazdırması bekleniyordu:

echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)

Ama bunun yerine sadece ilk satırı yazdırır: aaa.

Aynı şey -c( --bytes) seçeneğiyle kullanıldığında gerçekleşmez :

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)

Bu komut 1234512345beklendiği gibi çıkar. Ancak bu yalnızca yardımcı programın coreutils uygulamasında çalışır head. Busybox çıkışı sadece bu yüzden uygulama hala fazladan karakter yiyor 12345.

Sanırım bu özel uygulama yöntemi optimizasyon amacıyla yapıldı. Satırın nerede bittiğini bilemezsiniz, bu yüzden kaç karakter okumanız gerektiğini bilmezsiniz. Giriş akışından fazladan karakter tüketmemenin tek yolu, akış bayt baytını okumaktır. Ancak akıştan her seferinde bir bayt okumak yavaş olabilir. Bu yüzden headgiriş akışını yeterince büyük bir arabellek okur ve sonra bu arabellekteki satırları sayar.

Aynı şey --bytesseçenek kullanıldığında da söylenemez . Bu durumda kaç bayt okumanız gerektiğini bilirsiniz. Böylece tam olarak bu bayt sayısını okuyabilir ve bundan daha fazlasını değil. Corelibs uygulama bu fırsatı kullanır, ancak busybox bir değil, hala bir tampon içine gerekenden daha fazla bayt okur gelmez. Muhtemelen uygulamayı basitleştirmek için yapılır.

Yani soru. headYardımcı programın giriş akışından istenenden daha fazla karakter tüketmesi doğru mu? Unix yardımcı programları için bir tür standart var mı? Ve varsa, bu davranışı belirtir mi?

PS

Ctrl+CYukarıdaki komutları durdurmak için tuşuna basmanız gerekir. Unix yardımcı programları ötesinde okumada başarısız olmaz EOF. Tuşuna basmak istemiyorsanız daha karmaşık bir komut kullanabilirsiniz:

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)

basitlik için kullanmadım.


2
Neardupe unix.stackexchange.com/questions/48777/… ve unix.stackexchange.com/questions/84011/… . Ayrıca, bu başlık filmlerde olsaydı.SX cevabım Zardoz :) olurdu
dave_thompson_085

Yanıtlar:


30

Ana yardımcı programın giriş akışından istenenden daha fazla karakter tüketmesi doğru mu?

Evet, izin verilir (aşağıya bakın).

Unix yardımcı programları için bir tür standart var mı?

Evet, POSIX cilt 3, Shell & Utilities .

Ve varsa, bu davranışı belirtir mi?

Girişinde şunları yapar:

Standart bir yardımcı program aranabilir bir girdi dosyasını okuduğunda ve dosya sonuna gelmeden önce hatasız olarak sona erdiğinde, yardımcı program, açık dosya tanımındaki dosya ofsetinin, yardımcı program tarafından işlenen son baytın hemen ötesine düzgün bir şekilde konumlandırılmasını sağlamalıdır. Aranamayan dosyalar için, o dosyanın açık dosya açıklamasında dosya ofsetinin durumu belirtilmez.

headbiridir standart uygulamaların POSIX uygun uygulama davranışı yukarıda tarif edilen uygulamaya sahiptir, böylece.

GNU head gelmez doğru konumda dosya tanımlayıcısı bırakmak çalışıyorum, ama o, boruların böylece testinde bu konumunu geri başarısız aramaya imkansız. Bunu kullanarak şunları görebilirsiniz strace:

$ echo -e "aaa\nbbb\nccc\nddd\n" | strace head -n 1
...
read(0, "aaa\nbbb\nccc\nddd\n\n", 8192) = 17
lseek(0, -13, SEEK_CUR)                 = -1 ESPIPE (Illegal seek)
...

readDöner 17 bayt (tüm giriş), headbu dört işler ve sonra tekrar 13 bayt taşımak için çalışır, ancak olmaz. (Burada ayrıca GNU'nun head8 KiB tampon kullandığını da görebilirsiniz .)

Eğer anlattığım zaman head(standart dışı olan) bayt saymak için birçok bayt okumayı, o bilir bu yüzden can buna göre okuma sınırlamak (yolu, eğer uygulanırsa). Testinizin nedeni budur head -c 5: GNU headyalnızca beş baytı okur ve bu nedenle dosya tanımlayıcısının konumunu geri yüklemeye gerek yoktur.

Belgeyi bir dosyaya yazar ve bunun yerine kullanırsanız, peşinde olduğunuz davranışı elde edersiniz:

$ echo -e "aaa\nbbb\nccc\nddd\n" > file
$ < file (while true; do head -n 1; head -n 1 >/dev/null; done)
aaa
ccc

2
Birisi line(şimdi POSIX / XPG'den kaldırıldı, ancak yine de birçok sistemde kullanılabilir) veya read( IFS= read -r line) yardımcı programlarını kullanabilir ve bunun yerine sorunu önlemek için her seferinde bir bayt okuyabilir.
Stéphane Chazelas

3
head -c 55 bayt mı yoksa tam bir arabellek mi okunacağının uygulamaya bağlı olduğuna (ayrıca head -cstandart olmadığını da unutmayın ), buna güvenemeyeceğinizi unutmayın . En dd bs=1 count=5fazla 5 bayt okunacağının garantisine sahip olmanız gerekir .
Stéphane Chazelas

Thanks @ Stéphane, -c 5açıklamayı güncelledim .
Stephen Kitt

Not headait yerleşik ksh93bir seferde bir bayt okur head -n 1giriş aranabilir durumda iken.
Stéphane Chazelas

1
@anton_rh, ddyalnızca boru ile düzgün çalışır bs=1bir kullanırsanız count(eof ulaşıldığında sürece ancak en azından bir byte) istenenden daha az döndürebilir boruları üzerinde okur olarak. GNU ddsahip iflag=fullblockolduğu Gerçi zahmetinden.
Stéphane Chazelas

6

POSIX'ten

Kafa yarar istenen noktada her dosya için çıkış biten standart çıkışa onun girdi dosyaları kopyalamak olacaktır.

Girişten ne kadar head okunması gerektiği hakkında bir şey söylemez . Bayt-bayt okumasını istemek aptalca olurdu, çünkü çoğu durumda son derece yavaş olacaktır.

Bununla birlikte, bu readyerleşik / yardımcı programda ele alınmaktadır : readborulardan her seferinde bir bayt bulabildiğim tüm kabuklar ve standart metin , tek bir satırı okuyabilmek için bunun yapılması gerektiği anlamına gelebilir:

Okuma programı, bir ya da daha fazla kabuk değişkenleri standart giriş tek bir satır okunmalıdır.

Durumunda readkabuk komut kullanılır, ortak bir kullanım yeri, bu gibi bir şey olurdu:

read someline
if something ; then 
    someprogram ...
fi

Burada, standart girdisi someprogramkabuğunkiyle aynıdır, ancak tamponlanmış bir okumadan sonra kalan someprogramher şeyi değil, ilk girdi satırından sonra gelen her şeyi okuması beklenebilir . Öte yandan, örneğin örneğinde olduğu gibi kullanılması çok daha nadirdir.readreadhead


Gerçekten diğer tüm satırları silmek istiyorsanız, tek seferde tüm girdiyi işleyebilecek bazı araçları kullanmak daha iyi (ve daha hızlı) olacaktır, örn.

$ seq 1 10 | sed -ne '1~2p'   # GNU sed
$ seq 1 10 | sed -e 'n;d'     # works in GNU sed and the BSD sed on macOS

$ seq 1 10 | awk 'NR % 2' 
$ seq 1 10 | perl -ne 'print if $. % 2'

Ancak POSIX 3. cildine girişin “GİRİŞ DOSYALARI” bölümüne bakın ...
Stephen Kitt

1
POSIX diyor ki: "Standart bir yardımcı program aranabilir bir girdi dosyasını okuduğunda ve dosya sonuna gelmeden önce hatasız sonlandığında, yardımcı program açık dosya açıklamasındaki dosya ofsetinin, . yarar seekable olmayan dosyalar için, bu dosya için açık dosya açıklamasında ofset dosyanın durumu belirsizdir. "
AlexP

2
Kullanmak sürece Not -r, read(olmadan birden fazla satır okuyabilir IFS=aynı zamanda ön ve boşlukları ve sekmeleri sondaki (varsayılan değeri ile şerit olur $IFS)).
Stéphane Chazelas

@AlexP, evet, Stephen sadece bu kısmı bağladı.
ilkkachu

Not headait yerleşik ksh93bir seferde bir bayt okur head -n 1giriş aranabilir durumda iken.
Stéphane Chazelas

1
awk '{if (NR%2) == 1) print;}'

Hellóka :-) ve sitede hoş geldiniz! Not, daha ayrıntılı cevapları tercih ediyoruz. Geleceğin Google çalışanları için faydalı olmalılar.
peterh - Monica
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.