Grep'in -A -B -C anahtarlarına alternatif var mı (önce ve sonra birkaç satır yazdırmak için)?


10
grep -A 2 -B 3 

grep dizesinden sonra 2 satır ve daha önce 3 satır yazdırır.

grep -C 3

3 satır önce ve 3 satır sonra yazdırır

Ne yazık ki, grepkullanıyorum bu seçenekleri desteklemiyor. Bunu simüle etmek için alternatif komutlar veya komut dosyaları var mı? Kullanılması sed/ awk/ perl/ kabuk komut dosyalarını?


+1 Bir -Cgeçiş olduğunu bilmiyordum .
Lazer

1
GNU'nun grep'ini yükleyin. Daha genel olarak, yeni bir Sun makinesi buraya geldiğinde, kurulumdaki ilk adım birisinin adıydı GNU > /usr/local. GNU programları çok sayıda yararlı uzantıya sahiptir ve keyfi kısıtlamalardan kaçınmak için tasarlanmıştır (ancak boyut ve bazen performans açısından çok fazla ödeme yaparsınız). Birçok tescilli sistem, GNU ve diğer araçlarla "gayri resmi" paket depolarına sahiptir. "Ortak", satıcı tarafından
yönetilse

Yanıtlar:


6

Bunu yapmanın orta derecede çirkin bir yolu

grep -v pattern file >file.tmp; diff -c file.tmp file

veya bağlam satırları için -cile değiştirin . Yine de ekstra çıktı üretir. ( Destekleriniz / ise daha temiz olacaktır.)-C NUMNUMdiff-u-U NUM

Eğer senin diffyok -c/ -C/ -u, hala bunu yapmak için yolu vardır, ancak bunlar oldukça çirkin. Öte yandan, kimin bir sistem diffbile desteklemediği -cmuhtemelen Perl ya yoktur.


Bu harika, çekicilik gibi çalışır, ancak Windows oluşturulan dosyalar için çalışmak için bu ile -bitw seçeneği kullanmak zorunda kaldı.
Prashant Bhate

Stdin 'i diff' e gönderebilir ve geçici olarak atlayabilirsiniz:grep -v pattern file | diff -c - file
Cascabel

5

ack sadece Perl gerektirir ve içerir -A, -Bve -Cgrep en gibi bu işi seçenekleri. Perl'in grep yerine regex sözdizimini kullanır ve arama için dosyaları seçme biçimi oldukça farklıdır. Bu -fseçeneği kullanırken (aslında hiçbir şey aramadan arayacağı dosyaları yazdırır) seçeneği denemek isteyebilirsiniz .

Çekirdek olmayan modül gerektirmeyen tek bir komut dosyası olarak kurulabilir . Sadece ~/bindizininize (veya PATH'nizde yazma erişimine sahip olduğunuz başka bir yere) bırakın ve chmodçalıştırılabilir olduğundan emin olun .


Üretim kutusu ve Ne yazık ki bir şey yüklemek için yeterli ayrıcalığım yok ve risk
alamıyorum

@Prashant, ackkendi kullanımınız için yüklemek için kök gerekmez .
cjm

Evet ama yine de orada kullanamıyorum, ancak bu betiğin ~ /
bin'imde

@Prashant: Neden kullanamıyorsun? Sadece bir perl betiği.
intuited

1
ÜRETİM kutusu, üzerinde özel bir şey yapmak için özel izin onayları almalıdır. ve orada bir şeyler ters gidiyor kafamda geliyor;) ve Onun buna değmez :)
Prashant Bhate

5

Bu basit perl betiği grep -Abir dereceye kadar öykünür

#!/usr/bin/perl

$pattern=shift; #patthern to search
$lines=shift; # number of lines to print

$n = 0;
while (<>) {
  $n = $lines if /$pattern/; # reset counting
  if ($n) { print; $n-- } # print if within
  $n = 0 if eof; # don't leak across file boundaries
}

Komut dosyasını okunabilir ve kullanılabilir yapmak için bir kullanım ifadesi ekleyebileceğinizi unutmayın.)

USAGE:    $./grep-A.pl <pattern> <numLines> <filename> 

Güzel, bunu çalıştırmak için hangi perl versiyonuna ihtiyacım var?
Prashant Bhate

V5.10.1 kullanıyorum, sanırım perl 5 bugünlerde oldukça yaygın.
Vijay Anant

ya onun 5.8.8 ve işe yarıyor, harika, ama -B ne yapan bir senaryoya ihtiyacım var
Prashant Bhate

İyi. Yine de argümanların sırasını değiştirirdim; grep-A 3 fooçok daha doğal görünüyor grep-A foo 3. :-)
musiphil

3

Sadece GNU grep veya Ack yükleyebilirsiniz (Perl ile yazılmış, GNU grep'in birçok seçeneğini anlar ve daha fazlasını yapar).

Standart araçlara ve biraz komut dosyasına bağlı kalmayı tercih ederseniz , GNU grep ve seçeneklerinin davranışlarını taklit eden bir awk betiği . Minimum test edilmiştir.-A-B

#!/bin/sh
# grep-ac: a grep-like awk script
# Arguments: pattern = awk regexp to search for
#            before = number of lines to print before a match
#            after = number of lines to print after a match
{ "exec" "awk" "-f" "$0" "$@"; }
# The array h contains the history of lines that haven't been printed
# but are eligible for being "before" lines.
# The variable until contains the number of the last "after" line to print.
match($0, pattern) {   # the current line matches
    for (i in h) {
        print h[i];    # print each remaining before line
        delete h[i];   # delete each line as it's printed
    }
    until=NR+after;    # record the last after line to print
}
{
    if (NR<=until) print $0;    # from a match to its last after line: print
    else h[NR]=$0;              # after that: save in history
    delete h[NR-before];        # remove line too old to be a before line
}
END {exit !until}               # exit status: 0 if there was a match, else 1

Olarak çalıştır grep-ac -vpattern=PATTERN -vbefore=NBEFORE -vafter=NAFTERnerede PATTERN(bir aramak için kalıptır genişletilmiş düzenli ifade birkaçı ile awk eklemeler ) ve NBEFOREve NAFTERönce ve sırasıyla (0 varsaymak) bir maçtan sonra yazdırmak için çizgilerin sayılardır. Misal:

<input_file grep-ac -vbefore=2 -vpattern='foo *bar'

Veriyi dizide saklayan herhangi bir çözüm söz konusu değil ... daha önce de belirttiğim gibi, dosya boyutu oldukça büyük ve aşırı akabilir. Ayrıca bu sistemdeki awk, dosya boyutunun 3000 bayttan fazla olmasına izin vermez.
Prashant Bhate

2
@Prashant: İtirazlarınızı anlamıyorum. Bu komut dosyası satırları satır öncesi olmaya uygun olmadığında siler. Awk özel amaçlı bir programdan daha yüksek bir ek yüke sahip olabilmesi dışında gereksinimler göz önüne alındığında gereğinden fazla bellek kullanmaz (ancak aynı zamanda düşündüğünüz Perl'den daha az). Dosyanın toplam boyutu tamamen alakasızdır.
Gilles 'SO- kötü olmayı kes'

2
{ "exec" "awk" "-f" "$0" "$@"; }: shebang-line ayrıştırma sınırlamaları aşmanın çok şık yolu.
dubiousjim

2

Birbirini doğrudan takip eden eşleşen çizgiler olduğunda ortaya çıkan sorunlar nedeniyle -B'yi taklit etmenin oldukça zor olduğu ortaya çıkıyor. Bu, her türlü tek geçişli dosya taramasının kullanılmasına neredeyse izin vermez.

Bunu aşağıdaki yaklaşımla oynarken anladım:

perl -pe 'if(/search_term/) {print foreach @A; print ">"; $B=4}; shift @A if push(@A, $_)>7; $_ = "" unless ($B-- > 0);' target_file

Bu, birinci paragrafta açıklanan uyarı ile birlikte grep -A7 -B3'ün yaptığı gibi kabaca doğru şekilde çalışacaktır.

Bu sorunun alternatif (ayrıca tek dosya) çözümü, bir komut dizesini beslemek için perl kullanmaktır:

sed -n `perl -pe '$_=(/search_term/?sprintf("%d,%dp;", $.-3,$.+4):"")' file` file

oldukça uzun oneliner, ama, bu dosya çok büyük, bu yüzden bu durumda satırları diziye itmek kötü bir fikir, değil mi?
Prashant Bhate

shift @A if push(@A,$_)>7;Bit sadece maksimum boyut 7, etrafımızda bir dizi tutar. (bu sizin -A parametrenizdir). İkinci seçenek, inanılmaz derecede küçük bir dosyayı etrafında tutar (sadece orada üretilenleri görmek için sed dış katmanı olmadan perl'i çalıştırın), ancak dosyayı iki kez okur.
user455

0

Kullanarak sedönce eşleşen satırların satır numaralarını alabilir, bir whiledöngüde belirli bir satır numarasını azaltabilir ve arttırabilir ve ardından sed -n "n1,n2p"öndeki ( n1) ve izleyen ( n2) bağlamdaki satırları yazdırmak için kullanabilirsiniz ( seduser455 tarafından önerilen alternatife benzer ). Birçok okuma işlemi performans artışına yol açabilir.

edeşleşen bir satırın önceki ve sonraki satırlarına doğrudan başvurabilir, ancak belirtilen satır aralığı yoksa başarısız olur; örneğin, eşleşen satır 2 numaralı satırdır, ancak 5 maç öncesi satır yazdırılmalıdır. Bu ednedenle, başında ve sonunda uygun sayıda (boş) satır eklemek gerekir. (Büyük dosyalar ediçin doğru araç olmayabilir, bkz. Bfs - büyük dosya tarayıcı ).

# sample code to match lines with number 5 plus previous & following line
# (using Bash)
printf '%s\n' {1..20} > num.txt

# sed
sed -n '/5/=' num.txt | while read num; do
   n1=$((num - 1))
   n2=$((num + 1))
   [[ $n1 -lt 1 ]] && n1=1
   sed -n "${n1},${n2}p" num.txt
   echo --
done | sed -e '${/^--$/d;}'

# ed
cat <<-'EOF' | ed -s num.txt | sed -e $'N;N;a\\\n--' | sed -e '${/^--$/d;}'
H
0i
beginning: added line one
.
$a
end: added line one
.
,g/5/km\
'm-1,'m+1p
q
EOF
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.