Katakanlah saya memiliki file teks besar (> 2GB) dan saya hanya ingin cat
baris X
ke Y
(misalnya 57890000 hingga 57890010).
Dari apa yang saya mengerti saya bisa melakukan ini dengan menyalurkan head
ke tail
atau sebaliknya, yaitu
head -A /path/to/file | tail -B
atau sebagai alternatif
tail -C /path/to/file | head -D
di mana A
, B
, C
dan D
dapat dihitung dari jumlah baris dalam file, X
dan Y
.
Tetapi ada dua masalah dengan pendekatan ini:
- Anda harus menghitung
A
,B
,C
danD
. - Perintah dapat
pipe
untuk satu sama lain lebih banyak baris daripada yang saya tertarik untuk membaca (misalnya jika saya membaca hanya beberapa baris di tengah file besar)
Apakah ada cara agar shell berfungsi dengan baik dan menampilkan garis yang saya inginkan? (sambil hanya menyediakan X
dan Y
)?
tail
cat
large-files
head
Amelio Vazquez-Reina
sumber
sumber
Jawaban:
Saya menyarankan
sed
solusinya, tetapi demi kelengkapan,Untuk memotong setelah baris terakhir:
Tes kecepatan:
seq 100000000 > test.in
real
waktu seperti yang dilaporkan olehbash
builtintime
Ini sama sekali bukan tolok ukur yang tepat, tetapi perbedaannya jelas dan cukup berulang * untuk memberikan pemahaman yang baik tentang kecepatan relatif dari masing-masing perintah ini.
*: Kecuali di antara dua yang pertama,
sed -n p;q
danhead|tail
, yang pada dasarnya tampak sama.sumber
tail -n +50000000 test.in | head -n10
, yang tidak seperti itutail -n-50000000 test.in | head -n10
akan memberikan hasil yang benar?tail+|head
lebih cepat 10-15% dari sed, saya telah menambahkan patokan itu.-c
untuk melewati karakter,tail+|head
itu instan. Tentu saja, Anda tidak dapat mengatakan "50000000" dan mungkin harus mencari secara manual bagian awal yang Anda cari.Jika Anda ingin garis X ke Y inklusif (mulai penomoran pada 1), gunakan
tail
akan membaca dan membuang baris X-1 pertama (tidak ada jalan lain untuk itu), kemudian membaca dan mencetak baris berikut.head
akan membaca dan mencetak jumlah baris yang diminta, kemudian keluar. Ketikahead
keluar,tail
menerima sinyal SIGPIPE dan mati, sehingga tidak akan membaca lebih dari nilai ukuran buffer (biasanya beberapa kilobyte) dari baris dari file input.Atau, seperti yang disarankan gorkypl , gunakan sed:
Solusi sed secara signifikan lebih lambat (setidaknya untuk utilitas GNU dan utilitas Busybox; sed mungkin lebih kompetitif jika Anda mengekstrak sebagian besar file pada OS di mana pemipaannya lambat dan sednya cepat). Berikut adalah tolok ukur cepat di Linux; data yang dihasilkan oleh
seq 100000000 >/tmp/a
, lingkungannya adalah Linux / amd64,/tmp
adalah tmpfs dan mesinnya idle dan tidak bertukar.Jika Anda tahu rentang byte yang ingin Anda gunakan, Anda bisa mengekstraknya lebih cepat dengan melompati langsung ke posisi awal. Tetapi untuk baris, Anda harus membaca dari awal dan menghitung baris baru. Untuk mengekstrak blok dari x inklusif ke y mulai dari 0, dengan ukuran blok b:
sumber
tail will read and discard the first X-1 line
tampaknya harus dihindari ketika jumlah baris diberikan dari akhir, Dalam kasus seperti itu, ekor tampaknya membaca mundur dari ujung sesuai dengan waktu pelaksanaan. Silakan baca:http://unix.stackexchange.com/a/216614/79743
.tail
(termasuk ekor GNU) memiliki heuristik untuk dibaca dari akhir. Itu meningkatkantail | head
solusi dibandingkan dengan metode lain.The
head | tail
pendekatan adalah salah satu yang terbaik dan paling "idiomatik" cara untuk melakukan ini:Seperti yang ditunjukkan oleh Gilles dalam komentar, cara yang lebih cepat adalah
Alasan ini lebih cepat adalah X - 1 baris pertama tidak perlu melalui pipa dibandingkan dengan
head | tail
pendekatan.Pertanyaan Anda sebagai ungkapan agak menyesatkan dan mungkin menjelaskan beberapa kekhawatiran Anda yang tidak berdasar terhadap pendekatan ini.
Anda mengatakan Anda harus menghitung
A
,B
,C
,D
tapi seperti yang Anda lihat, jumlah baris dari file tidak diperlukan dan paling banyak 1 perhitungan diperlukan, yang shell dapat melakukannya untuk Anda anyways.Anda khawatir perpipaan akan membaca lebih banyak baris daripada yang diperlukan. Sebenarnya ini tidak benar:
tail | head
adalah tentang seefisien yang Anda dapatkan dari segi file I / O. Pertama, pertimbangkan jumlah minimum pekerjaan yang diperlukan: untuk menemukan baris ke - X dalam file, satu-satunya cara umum untuk melakukannya adalah membaca setiap byte dan berhenti ketika Anda menghitung simbol baris baru X karena tidak ada cara untuk membuat ilahi file offset dari garis X '. Setelah Anda mencapai garis * X * th, Anda harus membaca semua baris untuk mencetaknya, berhenti di baris Y '. Dengan demikian tidak ada pendekatan yang bisa lolos dengan membaca kurang dari Y baris. Sekarang,head -n $Y
baca tidak lebih dari Ygaris (dibulatkan ke unit penyangga terdekat, tetapi buffer jika digunakan dengan benar meningkatkan kinerja, jadi tidak perlu khawatir tentang overhead itu). Selain itu,tail
tidak akan membaca lebih darihead
, jadi dengan demikian kami telah menunjukkan bahwahead | tail
membaca jumlah baris paling sedikit mungkin (sekali lagi, ditambah beberapa buffering diabaikan yang kita abaikan). Satu-satunya keuntungan efisiensi dari pendekatan alat tunggal yang tidak menggunakan pipa adalah lebih sedikit proses (dan dengan demikian lebih sedikit overhead).sumber
Cara yang paling ortodoks (tetapi bukan yang tercepat, seperti dicatat oleh Gilles di atas) adalah menggunakan
sed
.Dalam kasus Anda:
The
-n
pilihan menyiratkan bahwa hanya pada baris yang bersangkutan dicetak ke stdout.The p pada akhir finishing nomor baris berarti untuk mencetak baris dalam kisaran yang diberikan. The q di bagian kedua dari script menghemat waktu dengan melompati sisa file.
sumber
sed
dantail | head
kira-kira setara, tetapi ternyatatail | head
secara signifikan lebih cepat (lihat jawaban saya ).tail
/head
dianggap lebih "ortodoks", karena pemangkasan kedua ujung file justru apa yang mereka dibuat untuk. Dalam bahan-bahan itu,sed
hanya tampak memasuki gambar ketika diperlukan penggantian - dan dengan cepat didorong keluar dari gambar ketika sesuatu yang jauh lebih kompleks mulai terjadi, karena sintaksnya untuk tugas-tugas kompleks jauh lebih buruk daripada AWK, yang kemudian mengambil alih .Jika kita tahu rentang untuk dipilih, dari baris pertama:
lStart
ke baris terakhir:lEnd
kita bisa menghitung:Jika kita tahu jumlah total baris:
lAll
kita juga bisa menghitung jarak ke akhir file:Maka kita akan tahu keduanya:
Memilih yang terkecil dari semua itu:
tailnumber
karena ini:Mengizinkan kami menggunakan perintah eksekusi tercepat yang konsisten:
Harap perhatikan tanda tambah tambah ("+") saat
$linestart
dipilih.Satu-satunya peringatan adalah bahwa kita memerlukan jumlah total garis, dan itu mungkin memerlukan waktu tambahan untuk menemukannya.
Seperti biasa dengan:
Beberapa waktu yang diukur adalah:
Perhatikan bahwa waktu berubah secara drastis jika garis yang dipilih mendekati awal atau dekat akhir. Perintah yang tampaknya berfungsi dengan baik di satu sisi file, mungkin sangat lambat di sisi lain file.
sumber
Saya cukup sering melakukan ini dan menulis skrip ini. Saya tidak perlu menemukan nomor baris, skrip melakukan semuanya.
sumber
tail|head
, yang telah dibahas secara luas dalam pertanyaan dan jawaban lainnya, dan 90% menentukan nomor baris tempat string / pola tertentu muncul, yang bukan bagian dari pertanyaan . PS Anda harus selalu mengutip parameter dan variabel shell Anda; mis., "$ 3" dan "$ 4".