Perintah shell berikut diharapkan untuk mencetak hanya garis ganjil dari aliran input:
echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)
Tapi bukannya itu hanya mencetak baris pertama: aaa
.
Hal yang sama tidak terjadi ketika digunakan dengan opsi -c
( --bytes
):
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)
Perintah ini menghasilkan 1234512345
seperti yang diharapkan. Tetapi ini hanya berfungsi dalam implementasi coreutils dari head
utilitas. The busybox pelaksanaan masih makan karakter tambahan, sehingga output hanya 12345
.
Saya kira cara implementasi khusus ini dilakukan untuk tujuan optimasi. Anda tidak bisa tahu di mana garis itu berakhir, jadi Anda tidak tahu berapa banyak karakter yang perlu Anda baca. Satu-satunya cara untuk tidak mengkonsumsi karakter tambahan dari aliran input adalah dengan membaca stream byte demi byte. Tetapi membaca dari aliran satu byte pada suatu waktu mungkin lambat. Jadi saya kira head
membaca input stream ke buffer yang cukup besar dan kemudian menghitung baris di buffer itu.
Hal yang sama tidak bisa dikatakan untuk kasus ketika --bytes
opsi digunakan. Dalam hal ini Anda tahu berapa byte yang perlu Anda baca. Jadi, Anda dapat membaca persis jumlah byte ini dan tidak lebih dari itu. The corelibs implementasi menggunakan kesempatan ini, tapi busybox satu tidak, masih membaca lebih byte dari yang dibutuhkan ke dalam buffer. Mungkin dilakukan untuk menyederhanakan implementasi.
Jadi pertanyaannya. Benarkah head
utilitas mengkonsumsi lebih banyak karakter dari aliran input daripada yang diminta? Apakah ada semacam standar untuk utilitas Unix? Dan jika ada, apakah ini menentukan perilaku ini?
PS
Anda harus menekan Ctrl+C
untuk menghentikan perintah di atas. Utilitas Unix tidak gagal membaca di luar EOF
. Jika Anda tidak ingin menekan, Anda dapat menggunakan perintah yang lebih kompleks:
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)
yang saya tidak gunakan untuk kesederhanaan.
sumber
Jawaban:
Ya, diizinkan (lihat di bawah).
Ya, POSIX volume 3, Shell & Utilities .
Itu, dalam pengantar:
head
adalah salah satu utilitas standar , sehingga implementasi POSIX-conforming harus mengimplementasikan perilaku yang dijelaskan di atas.GNU
head
memang mencoba untuk meninggalkan file deskriptor di posisi yang benar, tetapi tidak mungkin untuk mencari di pipa, jadi dalam pengujian Anda gagal untuk mengembalikan posisi. Anda dapat melihat ini menggunakanstrace
:The
read
pengembalian 17 byte (semua input yang tersedia),head
proses empat dari mereka dan kemudian mencoba untuk kembali 13 bytes, tetapi tidak bisa. (Anda juga dapat melihat di sini bahwa GNUhead
menggunakan buffer 8 KiB.)Ketika Anda memberi tahu
head
untuk menghitung byte (yang bukan standar), ia tahu berapa byte yang dibaca, sehingga ia dapat (jika diimplementasikan dengan cara itu) membatasi pembacaannya. Inilah mengapahead -c 5
tes Anda berfungsi: GNUhead
hanya membaca lima byte dan karenanya tidak perlu mencari untuk mengembalikan posisi deskriptor file.Jika Anda menulis dokumen ke file, dan menggunakannya, Anda akan mendapatkan perilaku yang Anda cari:
sumber
line
(sekarang dihapus dari POSIX / XPG tetapi masih tersedia di banyak sistem) atauread
(IFS= read -r line
) yang membaca satu byte pada suatu waktu untuk menghindari masalah.head -c 5
akan membaca 5 byte atau buffer penuh tergantung pada implementasinya (juga perhatikan bahwahead -c
itu bukan standar), Anda tidak dapat mengandalkan itu. Anda harusdd bs=1 count=5
memiliki jaminan bahwa tidak lebih dari 5 byte akan dibaca.-c 5
deskripsinya.head
bawaanksh93
membaca satu byte pada satu waktu denganhead -n 1
ketika input tidak dapat dicari.dd
hanya berfungsi dengan benar dengan pipabs=1
jika Anda menggunakan yangcount
dibaca di pipa dapat mengembalikan kurang dari yang diminta (tapi setidaknya satu byte kecuali jika eof tercapai). GNUdd
punyaiflag=fullblock
yang bisa meringankan itu.dari POSIX
Itu tidak mengatakan apa-apa tentang berapa banyak yang
head
harus dibaca dari input. Menuntutnya untuk membaca byte-by-byte akan konyol, karena akan sangat lambat dalam banyak kasus.Ini, bagaimanapun, dibahas dalam
read
builtin / utility: semua cangkang yang dapat saya temukanread
dari pipa satu byte pada suatu waktu dan teks standar dapat diartikan berarti bahwa ini harus dilakukan, untuk dapat membaca hanya satu baris:Dalam kasus
read
, yang digunakan dalam skrip shell, kasus penggunaan umum akan menjadi seperti ini:Di sini, input standar
someprogram
adalah sama dengan shell, tetapi dapat diharapkan bahwasomeprogram
akan membaca semua yang muncul setelah baris input pertama dikonsumsi olehread
dan bukan apa pun yang tersisa setelah buffered dibaca olehread
. Di sisi lain, menggunakanhead
seperti dalam contoh Anda jauh lebih jarang.Jika Anda benar-benar ingin menghapus setiap baris lain, akan lebih baik (dan lebih cepat) untuk menggunakan beberapa alat yang dapat menangani seluruh input sekaligus, misalnya
sumber
-r
,read
dapat membaca lebih dari satu baris (tanpaIFS=
itu juga akan menghapus spasi dan tab terkemuka (dengan nilai default$IFS
)).head
bawaanksh93
membaca satu byte pada satu waktu denganhead -n 1
ketika input tidak dapat dicari.sumber