Perintah unix cepat untuk menampilkan baris tertentu di tengah file?

206

Mencoba men-debug masalah dengan server dan satu-satunya file log saya adalah file log 20GB (bahkan tanpa stempel waktu! Mengapa orang menggunakan System.out.println()sebagai logging? Dalam produksi ?!)

Menggunakan grep, saya telah menemukan area file yang ingin saya lihat, baris 347340107.

Selain melakukan sesuatu seperti

head -<$LINENUM + 10> filename | tail -20 

... yang perlu headmembaca 347 juta baris pertama file log, adakah perintah cepat dan mudah yang akan membuang baris 347340100 - 347340200 (misalnya) ke konsol?

pembaruan Saya benar-benar lupa bahwa grep dapat mencetak konteks sekitar pertandingan ... ini berfungsi dengan baik. Terima kasih!

matt b
sumber
Saya akan membayangkan grep harus mencari seluruh file harus ada cara yang kurang intensif untuk melakukan ini.
ojblass

Jawaban:

69

dengan GNU-grep bisa Anda katakan

grep --context = 10 ...

sumber
7
Atau lebih khusus 10 baris sebelumnya: grep -B 10 ... Atau 10 baris setelah: grep -A 10 ...
Boy Baukema
17
Perintah ini tidak berfungsi, di bawah sed -n '<start>, <end> p' berfungsi
Basav
5
Ini sebenarnya bukan yang Anda inginkan karena akan memproses seluruh file bahkan jika pertandingan ada di bit atas. Pada titik ini kombo kepala / ekor atau ekor / kepala jauh lebih efektif.
Sklivvz
3
Ini tidak memenuhi pertanyaan yang diajukan sama sekali karena ini tidak menawarkan cara untuk menghasilkan baris tertentu , seperti yang ditanyakan.
Chris Rasys
1
Ini sebenarnya bukan yang diminta. @ tikar b, mengapa Anda tidak menerima jawaban ini?
user1271772
390

Saya menemukan dua solusi lain jika Anda tahu nomor baris tetapi tidak ada yang lain (tidak ada grep):

Dengan asumsi Anda membutuhkan jalur 20 hingga 40,

sed -n '20,40p;41q' file_name

atau

awk 'FNR>=20 && FNR<=40' file_name
Sklivvz
sumber
6
+1: Meskipun Anda mungkin ingin berhenti setelah mencetak. Mungkin menawarkan beberapa manfaat kinerja jika file tersebut sangat besar.
jaypal singh
awk 'NR> = 20 && NR <= 40' file_name
Sudipta Basak
2
sed -n '20, 40p; 41q 'nama file untuk berhenti kemudian.
Snigdha Batra
1
khusus, itu adalah angka garis awal dan akhir. Jika Anda berada di file yang lebih besar itu akan menjadi '12345678,12345699p'
Code Abominator
1
Selain itu untuk komentar @ CodeAbominator 41qmemerintahkan sed untuk berhenti pada baris 41.
Brice
116
# print line number 52
sed -n '52p' # method 1
sed '52!d' # method 2
sed '52q;d' # method 3,  efficient on large files 

Metode 3 efisien pada file besar

cara tercepat untuk menampilkan garis tertentu

WCC
sumber
Saya mencoba mencari cara untuk mengadaptasi metode 3 untuk menggunakan rentang alih-alih satu baris, tapi saya takut sed-foo saya tidak sesuai dengan tugas.
Xiong Chiamiov
9
@XiongChiamiov Bagaimana kalau sed -n '1.500p; 501q' untuk mencetak 1-500?
Sam
3
Alasan dua baris / metode pertama kurang efisien, adalah bahwa mereka terus memproses semua baris setelah Line 52, sampai akhir, sedangkan # 3 berhenti setelah mencetak Line 52.
flow2k
1
Jawaban ini akan mendapat manfaat dari menjelaskan apa yang dilakukan semua argumen.
Bram Vanroy
25

Tidak ada, file tidak dialamatkan dengan baris.

Tidak ada cara waktu konstan untuk menemukan awal baris n dalam file teks. Anda harus melakukan streaming melalui file dan menghitung baris baru.

Gunakan alat paling sederhana / tercepat yang harus Anda lakukan. Bagi saya, menggunakan headmembuat banyak lebih masuk akal daripada grep, karena yang terakhir ini cara yang lebih rumit. Saya tidak mengatakan " greplambat", sebenarnya tidak, tapi saya akan terkejut jika lebih cepat daripada headuntuk kasus ini. Itu akan menjadi bug pada headdasarnya.

beristirahat
sumber
2
Kecuali jika garis tetap lebar dalam byte, Anda tidak tahu di mana harus memindahkan penunjuk file tanpa menghitung karakter baris baru dari awal file.
Joseph Lust
Ini tidak memberikan jawaban untuk pertanyaan itu. Untuk mengkritik atau meminta klarifikasi dari penulis, tinggalkan komentar di bawah posting mereka.
exhuma
@ Alex Kau benar. Saya menulis ulang. Tujuh tahun yang lalu saya jengkel. :)
bersantai
20

Bagaimana dengan:

tail -n +347340107 filename | head -n 100

Saya tidak mengujinya, tetapi saya pikir itu akan berhasil.

itsmatt
sumber
Tidak, biasanya tail memiliki batas 256 kilobyte terakhir atau serupa, tergantung pada versi dan OS.
Antti Rytsölä
💪 yessire miller
dctremblay
13

Saya lebih suka masuk ke lessdan

  • mengetik 50%ke setengah file,
  • 43210G untuk pergi ke baris 43210
  • :43210 untuk melakukan hal yang sama

dan hal-hal seperti itu.

Bahkan lebih baik: tekan vuntuk mulai mengedit (dalam vim, tentu saja!), Di lokasi itu. Sekarang, perhatikan bahwa vimmemiliki ikatan kunci yang sama!

lihat
sumber
12

Saya pertama kali membagi file menjadi beberapa yang lebih kecil seperti ini

$ split --lines=50000 /path/to/large/file /path/to/output/file/prefix

dan kemudian klik pada file yang dihasilkan.

Luka Marinko
sumber
setuju, pecahkan log itu dan buat cron job untuk melakukannya dengan benar. gunakan logrotate atau sesuatu yang serupa agar mereka tidak menjadi sangat besar.
Tanj
9

Anda dapat menggunakan experintah, editor Unix standar (bagian dari Vim sekarang), mis

  • tampilkan satu baris (mis. baris kedua):

    ex +2p -scq file.txt

    sintaks sed yang sesuai: sed -n '2p' file.txt

  • rentang garis (mis. 2-5 baris):

    ex +2,5p -scq file.txt

    sintaks sed: sed -n '2,5p' file.txt

  • dari baris yang diberikan sampai akhir (mis. 5 hingga akhir file):

    ex +5,p -scq file.txt

    sintaks sed: sed -n '2,$p' file.txt

  • beberapa rentang garis (mis. 2-4 dan 6-8 baris):

    ex +2,4p +6,8p -scq file.txt

    sintaks sed: sed -n '2,4p;6,8p' file.txt

Perintah di atas dapat diuji dengan file tes berikut:

seq 1 20 > file.txt

Penjelasan:

  • +atau -cdiikuti oleh perintah - jalankan perintah (vi / vim) setelah file dibaca,
  • -s - mode diam, juga menggunakan terminal saat ini sebagai output default,
  • qdiikuti oleh -cadalah perintah untuk berhenti editor (tambahkan !untuk melakukan paksa berhenti, misalnya -scq!).
kenorb
sumber
7

Jika nomor baris Anda 100 untuk dibaca

head -100 filename | tail -1
Roopa
sumber
6

Dapatkan ack

Ubuntu / Debian instal:

$ sudo apt-get install ack-grep

Lalu lari:

$ ack --lines=$START-$END filename

Contoh:

$ ack --lines=10-20 filename

Dari $ man ack:

--lines=NUM
    Only print line NUM of each file. Multiple lines can be given with multiple --lines options or as a comma separated list (--lines=3,5,7). --lines=4-7 also works. 
    The lines are always output in ascending order, no matter the order given on the command line.
Odeyin
sumber
1
Bagi saya ini seperti perintah dengan sintaks yang paling intuitif dari semua jawaban di sini.
nzn
Dari versi 2.999_06 pada 10 Jan 2019 --linesparameter telah dihapus.
Burny
4

sed akan perlu membaca data juga untuk menghitung garis. Satu-satunya cara pintas dimungkinkan adalah adanya konteks / urutan file untuk beroperasi. Misalnya jika ada baris log yang diawali dengan waktu / tanggal lebar tetap, dll. Anda bisa menggunakan utilitas lihat unix untuk pencarian biner melalui file untuk tanggal / waktu tertentu

pixelbeat
sumber
4

Menggunakan

x=`cat -n <file> | grep <match> | awk '{print $1}'`

Di sini Anda akan mendapatkan nomor baris tempat pertandingan berlangsung.

Sekarang Anda dapat menggunakan perintah berikut untuk mencetak 100 baris

awk -v var="$x" 'NR>=var && NR<=var+100{print}' <file>

atau Anda bisa menggunakan "sed" juga

sed -n "${x},${x+100}p" <file>
Ramana Reddy
sumber
Jika Anda memiliki lebih dari satu pertandingan, gunakan: "awk 'NR == 1 {print $ 1}" untuk pertandingan pertama dan seterusnya
Ramana Reddy
2

Dengan sed -e '1,N d; M q'Anda akan mencetak garis N + 1 hingga M. Ini mungkin sedikit lebih baik daripada grep -Citu karena tidak mencoba untuk mencocokkan garis dengan suatu pola.

mweerden
sumber
-eopsional di sini.
flow2k
2

Membangun jawaban Sklivvz, inilah fungsi bagus yang bisa dimasukkan ke dalam .bash_aliasesfile. Ini efisien pada file besar saat mencetak barang dari depan file.

function middle()
{
    startidx=$1
    len=$2
    endidx=$(($startidx+$len))
    filename=$3

    awk "FNR>=${startidx} && FNR<=${endidx} { print NR\" \"\$0 }; FNR>${endidx} { print \"END HERE\"; exit }" $filename
}
Keithel
sumber
1

Untuk menampilkan garis dari <textfile>dengan nya <line#>, lakukan ini:

perl -wne 'print if $. == <line#>' <textfile>

Jika Anda ingin cara yang lebih kuat untuk menunjukkan serangkaian garis dengan ekspresi reguler - Saya tidak akan mengatakan mengapa grep adalah ide yang buruk untuk melakukan ini, itu seharusnya cukup jelas - ungkapan sederhana ini akan menunjukkan rentang Anda dalam single pass yang Anda inginkan ketika berhadapan dengan file teks ~ 20GB:

perl -wne 'print if m/<regex1>/ .. m/<regex2>/' <filename>

(tip: jika regex Anda ada /di dalamnya, gunakan sesuatu seperti m!<regex>!sebagai gantinya)

Ini akan mencetak <filename>mulai dengan garis yang cocok <regex1>hingga (dan termasuk) garis yang cocok <regex2>.

Tidak perlu penyihir untuk melihat bagaimana beberapa penyesuaian dapat membuatnya lebih kuat.

Hal terakhir: perl, karena merupakan bahasa yang matang, memiliki banyak perangkat tambahan tersembunyi untuk mendukung kecepatan dan kinerja. Dengan mengingat hal ini, menjadikannya pilihan yang jelas untuk operasi seperti itu karena pada awalnya dikembangkan untuk menangani file log besar, teks, database, dll.

osirisgothra
sumber
sungguh, sepertinya tidak seperti itu bagi saya, karena ketika menjalankan satu perintah perl lebih rumit daripada mengatakan, menjalankan 2+ program disalurkan bersama (lebih jauh ke bawah halaman), dan, saya pikir Anda benar-benar mengatakan karena saya mengetik lebih dari sebuah penjelasan yang mengharuskan Anda untuk BACA, karena ada halaman yang sama rumitnya (atau lebih) yang tidak meledak keluar dari air ... sheesh
osirisgothra
Perhatikan bahwa pengguna meminta serangkaian garis - contoh Anda dapat diadaptasi sepele.
Sklivvz
0

Anda dapat mencoba perintah ini:

egrep -n "*" <filename> | egrep "<line number>"
Fritz Dodoo
sumber
0

Mudah dengan perl! Jika Anda ingin mendapatkan baris 1, 3 dan 5 dari sebuah file, katakan / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
dagelf
sumber
1
Anda mengatakan itu mudah dengan awk, tetapi Anda melakukannya dalam perl sebagai gantinya?
Tahanan 13
0

Saya terkejut hanya satu jawaban lain (oleh Ramana Reddy) yang disarankan untuk menambahkan nomor baris ke output. Pencarian berikut untuk nomor baris yang diperlukan dan warna output.

file=FILE
lineno=LINENO
wb="107"; bf="30;1"; rb="101"; yb="103"
cat -n ${file} | { GREP_COLORS="se=${wb};${bf}:cx=${wb};${bf}:ms=${rb};${bf}:sl=${yb};${bf}" grep --color -C 10 "^[[:space:]]\\+${lineno}[[:space:]]"; }
belut ghEEz
sumber
Jawaban dengan kode hanya cenderung ditandai untuk dihapus. Bisakah Anda menambahkan beberapa komentar tentang bagaimana ini menyelesaikan masalah?
Graham