Apa cara terbaik untuk mengeluarkan segmen dari file teks?

13

Apa cara ekstraksi yang baik katakan, baris 20 -45 dari file teks besar. Tentu saja non-interaktif!

Chris Huang-Leaver
sumber

Jawaban:

12

kamu bisa mencoba:

cat textfile | head -n 45 | tail -n 26

atau

cat textfile | awk "20 <= NR && NR <= 45" 

memperbarui:

Seperti yang Mahomedalid tunjukkan, cattidak perlu dan sedikit berlebihan, tetapi itu membuat perintah yang bersih dan mudah dibaca.

Jika catmengganggu Anda, solusi yang lebih baik adalah:

<textfile awk "20 <= NR && NR <= 45"
Stefan
sumber
2
awk NR==20,NR==45 textfilebekerja juga, dan membaca dengan mudah.
ephemient
Saya lebih suka menggunakan stdin, ia memiliki konsistensi global dengan sisa nix
Stefan
1
Membaca dari argumen baris perintah memiliki konsistensi dengan utilitas UNIX lain juga, dan poin utama saya adalah untuk menunjukkan ,operator jangkauan awk .
ephemient
lol, maksudku @adam. tapi ya, saya suka saran Anda
Stefan
Saya pikir jawaban @ ephemient adalah yang terbaik di sini. Kalau tidak, perintahnya agak samar.
Léo Léopold Hertz 준영
13

Lebih sederhana:

sed -n '20,45p;45q' < textfile

Bendera -n menonaktifkan output default. "20,45" membahas baris 20 hingga 45, inklusif. Perintah "p" mencetak baris saat ini. Dan q berhenti setelah mencetak garis.

dkagedal
sumber
1
+1 bagus, saya Suka, tetapi baris 20 hingga 45 :)
Stefan
1
ok ok, saya mengeditnya untuk mengatakan 20,45 :-)
dkagedal
Menghapus qperintah (semuanya dimulai dari ;) meningkatkan kinerja bagi saya ketika mengekstraksi baris tunggal 26995107 dari file 27169334-line.
Ruslan
6

Ini bukan jawaban tetapi tidak bisa mempostingnya sebagai komentar.

Cara lain (sangat cepat) untuk melakukannya disarankan oleh mikeserv di sini :

{ head -n 19 >/dev/null; head -n 26; } <infile

Menggunakan file tes yang sama seperti di sini dan prosedur yang sama, berikut adalah beberapa tolok ukur (mengekstraksi baris 1000020-1000045):

mikeserv :

{ head -n 1000019 >/dev/null; head -n 26; } <iplist

real    0m0.059s

Stefan :

head iplist -n 1000045 | tail -n 26

real    0m0.054s

Sejauh ini, ini adalah solusi tercepat dan perbedaannya dapat diabaikan (untuk sekali lulus) (saya mencoba dengan rentang yang berbeda: beberapa baris, jutaan baris dll).

Melakukannya tanpa pipa mungkin menawarkan keuntungan yang signifikan, namun, untuk aplikasi yang perlu mencari beberapa rentang garis dengan cara yang sama, seperti:

for  pass in 0 1 2 3 4 5 6 7 8 9
do   printf "pass#$pass:\t"
     head -n99 >&3; head -n1
done <<1000LINES 3>/dev/null
$(seq 1000)
1000LINES

... yang mencetak ...

pass#0: 100
pass#1: 200
pass#2: 300
pass#3: 400
pass#4: 500
pass#5: 600
pass#6: 700
pass#7: 800
pass#8: 900
pass#9: 1000

... dan hanya membaca file melalui satu waktu.


Yang lain sed/ awk/ perlsolusi membaca seluruh file dan karena ini adalah tentang file besar, mereka tidak sangat efisien. Saya melemparkan beberapa alternatif yang exitatau qsesuai dengan baris terakhir dalam rentang yang ditentukan:

Stefan :

awk "1000020 <= NR && NR <= 1000045" iplist

real    0m2.448s

vs.

awk "NR >= 1000020;NR==1000045{exit}" iplist

real    0m0.243s

dkagedal ( sed):

sed -n 1000020,1000045p iplist

real    0m0.947s

vs.

sed '1,1000019d;1000045q' iplist

real    0m0.143s

Steven D :

perl -ne 'print if 1000020..1000045' iplist

real    0m2.041s

vs.

perl -ne 'print if $. >= 1000020; exit if $. >= 1000045;' iplist

real    0m0.369s
don_crissti
sumber
+1 Saya pikir ini adalah jawaban terbaik di sini! Akan menyenangkan untuk mendapatkan berapa banyak waktu dengan ini awk NR==1000020,NR==1000045 textfiledi sistem Anda.
Léo Léopold Hertz 준영
3
ruby -ne 'print if 20 .. 45' file
pengguna1606
sumber
1
seorang rekan rubyist, Anda mendapatkan suara saya sir
Stefan
1
Sementara kita melakukannya, mengapa tidak python -c 'import fileinput, sys; [sys.stdout.write(line) for nr, line in enumerate(fileinput.input()) if 19 <= nr <= 44]'juga? :-P Ini adalah sesuatu yang dapat dilakukan Ruby, dengan model Perl, terinspirasi oleh awk / sed, dapat dilakukan dengan mudah.
ephemient
2

Karena sed dan awk sudah digunakan, berikut adalah solusi perl:

perl -nle "print if ($. > 19 && $. < 46)" < textfile

Atau, seperti yang ditunjukkan dalam komentar:

perl -ne 'print if 20..45' textfile
Steven D
sumber
2
Ada apa dengan semua karakter ekstra itu? Tidak perlu menghapus dan menambahkan kembali baris baru, flip-flop mengasumsikan perbandingan dengan nomor baris, dan operator berlian menjalankan argumen jika disediakan. perl -ne'print if 20..45' textfile
ephemient
1
Bagus. -Tidak sedikit refleks kurasa, seperti untuk sisanya, aku tidak punya alasan kecuali ketidaktahuan.
Steven D