Grepowe postacie przed i po meczu?

144

Używając tego:

grep -A1 -B1 "test_pattern" file

wyświetli jedną linię przed i po dopasowanym wzorcu w pliku. Czy istnieje sposób, aby wyświetlić nie wiersze, ale określoną liczbę znaków?

Linie w moim pliku są dość duże, więc nie interesuje mnie drukowanie całej linii, a raczej obserwowanie dopasowania w kontekście. Jakieś sugestie, jak to zrobić?

Legenda
źródło
1
Duplikat unix.stackexchange.com/q/163726 Prawie duplikat stackoverflow.com/q/2034799
sondra.kinsey

Odpowiedzi:

184

3 znaki przed i 4 znaki po

$> echo "some123_string_and_another" | grep -o -P '.{0,3}string.{0,4}'
23_string_and
ДМИТРИЙ МАЛИКОВ
źródło
5
Dobra odpowiedź dla małych ilości danych, ale zaczyna działać wolno, gdy dopasowujesz> 100 znaków - np. W moim gigantycznym pliku xml chcę {1200} przed i po, i jest zbyt wolny w użyciu.
Benubird
3
Wersja awk autorstwa @amit_g jest znacznie szybsza.
ssobczak
6
Niedostępne w systemie Mac OSX, więc tak naprawdę nie jest to powszechnie dostępne rozwiązanie. Wersja -E (wymieniona poniżej) jest lepszym rozwiązaniem. Co to jest -P? Czytaj dalej ... -P, --perl-regexp Interpretuje WZORZEC jako wyrażenie regularne Perla (PCRE, zobacz poniżej). Jest to wysoce eksperymentalne i grep -P może ostrzegać o niezaimplementowanych funkcjach.
Xofo
2
Na OSX zainstaluj przez: brew install homebrew/dupes/grepi uruchom jako ggrep.
kenorb
1
Jak zasugerował @Benubird, będzie to niemożliwe do użycia w przypadku dużych plików z umiarkowanie szerokim otoczeniem pożądanym dla celu dopasowania.
matanster
113
grep -E -o ".{0,5}test_pattern.{0,5}" test.txt 

Dopasuje do 5 znaków przed i po wzorze. Przełącznik -o mówi grepowi, aby wyświetlał tylko dopasowanie, a -E, aby używał rozszerzonego wyrażenia regularnego. Upewnij się, że wyrażenie zostało otoczone cudzysłowami, w przeciwnym razie powłoka może je zinterpretować.

ekse
źródło
1
Dobra odpowiedź, to ciekawe, że ograniczona do 2 ^ 8-1 dla długości w {} tak {0,255}dzieł {0,256}dajegrep: invalid repetition count(s)
CodeMonkey
Wydaje się, że staje się to znacznie mniej wydajne, gdy zwiększam liczbę pasujących znaków (5 -> 25 -> 50), jakiś pomysł dlaczego?
Adam Hughes
37

Możesz użyć

awk '/test_pattern/ {
    match($0, /test_pattern/); print substr($0, RSTART - 10, RLENGTH + 20);
}' file
amit_g
źródło
2
Ładnie działa również z nieco większymi plikami
Touko
4
jak możesz tego użyć, aby znaleźć wiele dopasowań w wierszu?
koox00
1
Jakie jest znaczenie pierwszej liczby w parach w nawiasach klamrowych? Podobnie jak 0 w „grep -E -o”. {0,5} test_pattern. {0,5} „test.txt”?
Lew Rockwell Fan
To naprawdę szybsze, ale nie tak dokładne, jak odpowiedź @ ekse.
Abdollah
24

Masz na myśli tak:

grep -o '.\{0,20\}test_pattern.\{0,20\}' file

?

Spowoduje to wydrukowanie do dwudziestu znaków po obu stronach test_pattern. \{0,20\}Notacja jest podobny *, ale określa zera do dwudziestu powtórzeń zamiast zero lub more.The -omówi pokazać tylko sam mecz, a nie całej linii.

ruakh
źródło
To polecenie nie działa dla mnie:grep: Invalid content of \{\}
Alexander Pravdin
0

Za pomocą gawkmożesz użyć funkcji dopasowania:

    x="hey there how are you"
    echo "$x" |awk --re-interval '{match($0,/(.{4})how(.{4})/,a);print a[1],a[2]}'
    ere   are

Jeśli nie masz nic perlprzeciwko, bardziej elastycznemu rozwiązaniu: Następujące spowoduje wydrukowanie trzech znaków przed wzorem, a następnie faktycznego wzoru i 5 znaków po wzorze.

echo hey there how are you |perl -lne 'print "$1$2$3" if /(.{3})(there)(.{5})/'
ey there how

Można to również zastosować do słów zamiast samych znaków, a następnie wypisze jedno słowo przed właściwym pasującym ciągiem.

echo hey there how are you |perl -lne 'print $1 if /(\w+) there/'
hey

Następujące spowoduje wydrukowanie jednego słowa po wzorze:

echo hey there how are you |perl -lne 'print $2 if /(\w+) there (\w+)/'
how

Następujące spowoduje wydrukowanie jednego słowa przed wzorcem, następnie właściwego słowa, a następnie jednego słowa po wzorcu:

echo hey there how are you |perl -lne 'print "$1$2$3" if /(\w+)( there )(\w+)/'
hey there how
P ....
źródło
0

Możesz użyć wyrażenia regularnego grep do znalezienia + drugiego grep do wyróżnienia

echo "some123_string_and_another" | grep -o -P '.{0,3}string.{0,4}' | grep string

23_string_and

wprowadź opis obrazu tutaj

Andrew Zhilin
źródło
0

Nigdy nie zapamiętam tych tajemniczych modyfikatorów poleceń, więc wybrałem najlepszą odpowiedź i przekształciłem ją w funkcję w moim ~/.bashrcpliku:


cgrep() {
    # For files that are arrays 10's of thousands of characters print.
    # Use cpgrep to print 30 characters before and after search patttern.
    if [ $# -eq 2 ] ; then
        # Format was 'cgrep "search string" /path/to/filename'
        grep -o -P ".{0,30}$1.{0,30}" "$2"
    else
        # Format was 'cat /path/to/filename | cgrep "search string"
        grep -o -P ".{0,30}$1.{0,30}"
    fi
} # cgrep()

Oto jak to wygląda w akcji:

$ ll /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

-rw-r--r-- 1 rick rick 25780 Jul  3 19:05 /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

$ cat /tmp/rick/scp.Mf7UdS/Mf7UdS.Source | cgrep "Link to iconic"

1:43:30.3540244000 /mnt/e/bin/Link to iconic S -rwxrwxrwx 777 rick 1000 ri

$ cgrep "Link to iconic" /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

1:43:30.3540244000 /mnt/e/bin/Link to iconic S -rwxrwxrwx 777 rick 1000 ri

Plik, o którym mowa, to jedna ciągła linia 25K i beznadziejne jest znalezienie tego, czego szukasz, używając zwykłego grep.

Zwróć uwagę na dwa różne sposoby wywołania cgreptej grepmetody paraleli .

Istnieje „ładniejszy” sposób tworzenia funkcji, w której „$ 2” jest przekazywane tylko wtedy, gdy jest ustawiona, co oszczędziłoby 4 wiersze kodu. Nie mam go jednak pod ręką. Coś jak ${parm2} $parm2. Jeśli ją znajdę, zrewiduję funkcję i tę odpowiedź.

WinEunuuchs2Unix
źródło