Bir dosyanın ortasında belirli satırları görüntülemek için hızlı unix komutu?


206

Bir sunucuyla ilgili bir sorunu ayıklamaya çalışmak ve tek günlük dosyam 20GB günlük dosyasıdır (zaman damgası bile olmadan! İnsanlar neden System.out.println()günlük olarak kullanır ? Üretimde ?!)

Grep kullanarak, dosyanın 347340107 satırına bakmak istediğim bir alanını buldum.

Gibi bir şey yapmaktan başka

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

... headgünlük dosyasının ilk 347 milyon satırını okumayı gerektiren , 347340100 - 347340200 (örneğin) satırlarını konsola dökecek hızlı ve kolay bir komut var mı?

güncelleştirmek bu iyi çalışıyor ... Ben tamamen bu grep bir maç etrafındaki bağlamı yazdırabilirsiniz unuttum. Teşekkürler!


Grep tüm dosyayı aramak zorunda hayal ediyorum bunu yapmak için bir cpu daha az yoğun bir şekilde olmalı.
ojblass

Yanıtlar:


69

GNU-grep ile

grep --context = 10 ...

7
Veya daha spesifik olarak 10 satır önce: grep -B 10 ... Veya 10 satır sonra: grep -A 10 ...
Boy Baukema

17
Bu komut çalışmıyor, sed -n '<start>, <end> p' altında çalışıyor
Basav

5
Bu aslında istediğiniz bir şey değildir , çünkü maç en üst bitde olsa bile tüm dosyayı işleyecektir. Bu noktada bir kafa / kuyruk veya kuyruk / kafa kombinasyonu çok daha etkilidir.
Sklivvz

3
Bu, sorulan soruyu tatmin etmez, çünkü bu, istendiği gibi belirli bir satırın çıktısını almanın bir yolunu sunmaz .
Chris Rasys

1
Aslında sorulan bu değil. @matt b, neden bu yanıtı kabul etmiyorsun?
user1271772

390

Satır numarasını biliyorsanız ama başka bir şey bilmiyorsanız iki çözüm buldum (grep mümkün değil):

20-40 arası hatlara ihtiyacınız olduğunu varsayarsak,

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

veya

awk 'FNR>=20 && FNR<=40' file_name

6
+1: Yazdırdıktan sonra çıkmak isteyebilirsiniz. Dosya gerçekten çok büyükse bazı performans avantajları sunabilir.
jaypal singh

awk 'NR> = 20 && NR <= 40' dosya_adı
Sudipta Basak

2
sed -n '20, 40p; 41q 'dosya_adı o zaman çıkmak için.
Snigdha Batra

1
özellikle, bunlar başlangıç ​​ve bitiş satır numaralarıdır. Daha büyük bir
dosyadaysanız

1
Ek olarak @ CodeAbominator yorumuna 41qsed on line çıkmak için talimat 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 

yöntem 3 büyük dosyalar için verimli

belirli satırları görüntülemenin en hızlı yolu


Tek bir satır yerine bir aralık kullanmak için yöntem 3 adapte nasıl anlamaya çalışıyorum, ama korkarım sed-foo göreve kadar değil.
Xiong Chiamiov

9
@XiongChiamiov 1-500 yazdırmak için sed -n '1,500p; 501q' ye ne dersiniz?
Sam

3
İlk iki satırın / yöntemin daha az verimli olmasının nedeni, Satır 52'yi yazdırdıktan sonra # 3 durak, Satır 52'yi yazdırdıktan sonra # 3 durak, Satır 52'den sonra tüm satırları işlemeye devam
etmeleridir

1
Bu cevap, tüm argümanların ne yaptığını açıklamaktan fayda sağlayacaktır.
Bram Vanroy

25

Hayır, dosyalar satır adreslenemez.

Metin dosyasında n satırının başlangıcını bulmanın sabit zamanlı bir yolu yoktur . Dosya üzerinden akış yapmalı ve satır sonlarını saymalısınız.

İşi yapmanız gereken en basit / en hızlı aracı kullanın. Bana göre kullanarak headyapar çok daha mantıklı grepikincisi yol daha karmaşık olduğundan,. " grepYavaş" demiyorum , gerçekten değil, ama headbu davadan daha hızlı olursa şaşırırdım. headTemelde bir hata olurdu .


2
Çizgiler genişlik bayt cinsinden sabit değilse, dosyanın başlangıcından itibaren yeni satır karakterleri saymadan dosya işaretçisini nereye taşıyacağınızı bilmezsiniz.
Joseph Lust

Bu soruya bir cevap sağlamaz. Bir yazardan eleştiri veya açıklama istemek için gönderilerinin altına bir yorum bırakın.
exhuma

@exhuma Haklısın. Yeniden yazdım. Yedi yıl önce bıktım. :)
gevşeyin

20

Ne dersin:

tail -n +347340107 filename | head -n 100

Test etmedim, ama bunun işe yarayacağını düşünüyorum.


Hayır, genellikle kuyruk, sürüme ve işletim sistemine bağlı olarak son 256 kilobayt veya benzeri bir limite sahiptir.
Antti Rytsölä

💪 yessire miller
dctremblay

13

Sadece girmeyi tercih ediyorum lessve

  • 50%dosyanın yarısına gitmek için yazmak ,
  • 43210G 43210 numaralı satıra gitmek için
  • :43210 aynısını yapmak

ve onun gibi şeyler.

Daha da iyisi: vo konumda düzenlemeye başlamak için (vim'de, elbette!) Vurun. Şimdi, vimaynı anahtar bağlamaları olduğunu unutmayın !


12

Önce dosyayı böyle daha küçük dosyalara bölerdim

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

ve sonuçta ortaya çıkan dosyaları grep.


kabul ettiyseniz, bu kaydı kapatın ve düzgün bir şekilde yapmak için bir cron işi oluşturun. bu kadar büyük olmalarını önlemek için logrotate veya benzeri bir şey kullanın.
tanj

9

exStandart bir Unix düzenleyicisi (şimdi Vim'in bir parçası) komutunu kullanabilirsiniz , örn.

  • tek bir satır görüntüleme (ör. ikinci satır):

    ex +2p -scq file.txt

    karşılık gelen sed sözdizimi: sed -n '2p' file.txt

  • hat aralığı (örn. 2-5 hat):

    ex +2,5p -scq file.txt

    sed sözdizimi: sed -n '2,5p' file.txt

  • verilen satırdan sonuna kadar (örneğin dosyanın 5. sonuna kadar):

    ex +5,p -scq file.txt

    sed sözdizimi: sed -n '2,$p' file.txt

  • çoklu çizgi aralıkları (örneğin 2-4 ve 6-8 çizgi):

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

    sed sözdizimi: sed -n '2,4p;6,8p' file.txt

Yukarıdaki komutlar aşağıdaki test dosyasıyla test edilebilir:

seq 1 20 > file.txt

Açıklama:

  • +veya -cardından komut - dosya okunduktan sonra (vi / vim) komutunu yürütün,
  • -s - sessiz mod, ayrıca varsayılan çıkış olarak akım terminalini kullanır,
  • qardından -ceditörden çıkma komutu ( !do to force force quit'e ekle -scq!).

7

Satır numaranız okunacak 100 ise

head -100 filename | tail -1

6

Almak ack

Ubuntu / Debian kurulumu:

$ sudo apt-get install ack-grep

O zaman koş:

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

Misal:

$ ack --lines=10-20 filename

Gönderen $ 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.

1
Bu, bana göre buradaki tüm cevaplardan en sezgisel sözdizimine sahip komut gibi görünüyor.
nzn

10 Ocak 2019'daki 2.999_06 sürümünden --linesparametre kaldırıldı.
burny

4

sed satırları saymak için verileri de okumalıdır. Bir kısayolun mümkün olabilmesinin tek yolu, dosyada çalışacak bağlam / düzen olabilir. Örneğin, sabit genişlikli saat / tarih vb. İle eklenmiş günlük satırları varsa , belirli tarihler / saatler için dosyalar arasında ikili arama yapmak için look unix yardımcı programını kullanabilirsiniz.


4

kullanım

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

Burada maçın gerçekleştiği satır numarasını alacaksınız.

Şimdi 100 satır yazdırmak için aşağıdaki komutu kullanabilirsiniz

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

ya da "sed" kullanabilirsiniz

sed -n "${x},${x+100}p" <file>

Birden fazla eşleşmeniz varsa kullanın: "awk 'NR == 1 {print $ 1}" ilk maç ve benzeri için
Ramana Reddy

2

İle sed -e '1,N d; M q'Eğer satırları yazdırmak edeceğiz M. This aracılığıyla N + 1, muhtemelen biraz daha iyi daha sonra grep -Cbir modele hatları maç için çalışmaz olarak.


-eburada isteğe bağlıdır.
flow2k

2

Sklivvz'ın cevabına dayanarak, bir .bash_aliasesdosyaya koyabileceğiniz hoş bir işlev . Dosyanın ön tarafından bir şeyler yazdırırken büyük dosyalarda etkilidir.

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
}

1

Bir satırını göstermek için <textfile>tarafından onun<line#> , sadece bu yapın:

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

Düzenli ifadeleri olan bir dizi satırı göstermenin daha güçlü bir yolunu istiyorsanız - grep'in bunu yapmak için neden kötü bir fikir olduğunu söylemeyeceğim, oldukça açık olmalı - bu basit ifade size bir aralıkta ~ 20GB metin dosyaları ile uğraşırken istediğiniz tek geçiş:

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

(ipucu: normal ifadeniz varsa / , bunun gibi bir şey m!<regex>!kullanın)

Bu <filename>, eşleşen çizgiden başlayarak yazdırılır<regex1> kadar (ve dahil)<regex2> .

Birkaç ince ayarın onu nasıl daha güçlü hale getirebileceğini görmek bir sihirbaz gerektirmez.

Son şey: perl, olgun bir dil olduğundan, hız ve performans lehine birçok gizli geliştirmeye sahiptir. Bunu göz önünde bulundurarak, başlangıçta büyük günlük dosyalarını, metni, veritabanlarını vb. İşlemek için geliştirildiğinden, böyle bir işlem için bariz bir seçimdir.


gerçekten, bana öyle görünmüyor, çünkü bir perl komutunu çalıştırdığından daha karmaşık, 2 + programları birbirine bağlı (sayfanın aşağısında) çalıştırmak ve sanırım aslında söylüyorsunuz çünkü Suyun dışarı
üflenmedi

Kullanıcının bir dizi satır istediğini unutmayın - örneğiniz önemsiz bir şekilde uyarlanabilir.
Sklivvz

0

Bu komutu deneyebilirsiniz:

egrep -n "*" <filename> | egrep "<line number>"

0

Perl ile kolay! Bir dosyadan satır 1, 3 ve 5'i almak istiyorsanız, / etc / passwd deyin:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

1
Awk ile kolay olduğunu söylüyorsun, ama bunu perl'de mi yaptın?
Mahkum 13

0

(Ramana Reddy tarafından) çıktıya satır numaraları eklemeyi öneren sadece bir cevap daha şaşırdım. Aşağıdakiler, gerekli satır numarasını arar ve çıktıyı renklendirir.

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:]]"; }

Kodlu yanıtlar yalnızca silinmek üzere işaretlenir. Bunun sorunu nasıl çözdüğüne dair bir yorum ekleyebilir misiniz?
Graham
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.