Znajdź wszystkie wystąpienia w pliku z sed

15

Korzystanie z OPEN STEP 4.2 OS ... Obecnie używam następującego sedpolecenia:

sed -n '1,/141.299.99.1/p' TESTFILE | tail -3

To polecenie znajdzie jedno wystąpienie w pliku o numerze ip 141.299.99.1, a także zawiera 3 wiersze przed nim, co jest dobre, z wyjątkiem tego, że chciałbym również znaleźć wszystkie wystąpienia adresu IP i 3 wiersze przed nim i nie tylko pierwszy.

Dołek
źródło
1
Należy zawsze podać swój system operacyjny. Rozwiązania bardzo często zależą od używanego systemu operacyjnego. Czy korzystasz z Uniksa, Linuksa, BSD, OSX, czy czegoś innego? Która wersja
terdon
WIELKI PUNKT! Korzystanie z Open Step w wersji 4.2 jest dość stare, a zawarte w nim powłoki nie zawierają wielu funkcji wymienionych w odpowiedziach poniżej.
Dale
Z ciekawości - co to jest system OPEN STEP 4.2 i do czego jest używany dzisiaj?
Thorbjørn Ravn Andersen
(a jeśli Perl jest dostępny, możesz naprawdę zrobić wiele fajnych rzeczy)
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Może to jest to: en.wikipedia.org/wiki/OpenStep
Barmar

Odpowiedzi:

4

Oto próba emulacji grep -B3za pomocą ruchomego okna sed, na podstawie tego przykładu GNU sed (ale mam nadzieję, że jest zgodny z POSIX - z potwierdzeniem @ StéphaneChazelas):

sed -e '1h;2,4{;H;g;}' -e '1,3d' -e '/141\.299\.99\.1/P' -e '$!N;D' file

Pierwsze dwa wyrażenia przygotowują bufor wielowierszowy i pozwalają na obsługę wielkości krawędzi, w której przed pierwszym dopasowaniem znajdują się mniej niż 3 linie poprzedniego kontekstu. Wyrażenie środkowe (dopasowanie wyrażenia regularnego) drukuje linię poza górną krawędzią okna, aż żądany tekst dopasowania zostanie pomarszczony przez bufor wzorców. Końcowy $!N;Dprzewija okno o jedną linię, chyba że osiągnie koniec wprowadzania.

steeldriver
źródło
-enie jest specyficzne dla GNU. Aby być POSIX / przenośnym, potrzebujesz go, ponieważ nic nie może być później }(i potrzebujesz ;przed nim).
Stéphane Chazelas
Dzięki @ StéphaneChazelas - czy więc mówisz, że aby być POSIX / przenośnym, pierwsza grupa musi zostać podzielona / zmodyfikowana jako -e '1h;2,4{H;g;}' -e '1,3d'? Nie mam systemu innego niż GNU do testowania (i --posixwydaje się, że przełącznik GNU sed nie obchodzi).
steeldriver
1
Tak, w Linuksie możesz przetestować inną implementację, korzystając z podręcznego sedzestawu narzędzi, który jest potomkiem tradycyjnego uniksowego seda. POSIX / Unix spec sedjest pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
Stéphane Chazelas
Otrzymuję zdarzenie nie znalezione na żadnym z poniższych: N; D ': Nie znaleziono zdarzenia. Czy brakuje mi gdzieś składni? Dzięki!!
Dale
Niestety, właśnie zdałem sobie sprawę, że moja ostatnia edycja pominęła zamykający pojedynczy cytat po pierwszym wyrażeniu -e. Poprawiłem to teraz - czy możesz spróbować ponownie z powyższym wyrażeniem?
steeldriver
10

grep zrobi to lepiej:

grep -B 3 141.299.99.1 TESTFILE

Te -B 3środki , aby wydrukować trzy linie przed każdym meczem. Zostanie wydrukowany --między każdą grupą linii. Aby to wyłączyć, użyj --no-group-separatorrównież.

-BOpcja jest obsługiwana przez GNUgrep i większości wersji BSD, jak również ( OSX , FreeBSD , OpenBSD , NetBSD ), ale to nie jest technicznie standardowym rozwiązaniem.

Michael Homer
źródło
1
Michael Homer - dziękuję. Nie mam opcji - B. Jakieś inne pomysły?
Dale
@Dale Czy możesz zainstalować GNU grep? To da ci opcję.
Barmar
9

Dzięki sedniemu możesz zrobić przesuwane okno.

sed '1N;$!N;/141.299.99.1/P;D'

To wystarczy Ale uwaga - bashszalone zachowanie rozwija się, ! nawet gdy jest cytowane !!! do ciągu poleceń z historii poleceń może sprawić, że trochę oszaleje. W set +H;przypadku znalezienia takiej sytuacji należy poprzedzić ją komendą . Aby następnie ponownie włączyć (ale dlaczego ???) zrobić set -Hpóźniej.

To, oczywiście, miałaby zastosowanie tylko wtedy, gdy zostały użyciu bash- choć nie wierzę jesteś. Jestem całkiem pewien, że pracujesz csh- (która okazuje się być powłoką, której szalone zachowanie bashnaśladuje ekspansję historii, ale być może nie do końca, jak to c) . Więc prawdopodobnie\! powinno działać. Mam nadzieję.

To wszystko przenośny kod: POSIX opisuje w ten sposób swoje trzy operatory: (chociaż warto zauważyć, że potwierdziłem, że ten opis istniał już w 2001 roku)

[2addr]N Dołącz następny wiersz danych wejściowych, pomniejszając \nkońcową ewlinię, do przestrzeni wzoru, używając osadzonej \newline, aby oddzielić dołączony materiał od materiału oryginalnego. Zauważ, że bieżący numer linii zmienia się.

[2addr]P Zapisz przestrzeń wzorca, aż do pierwszego \nwiersza, na standardowe wyjście.

[2addr]D Usuń początkowy segment przestrzeni wzorów przez pierwszą \nlinię ewline i rozpocznij następny cykl.

Tak więc w pierwszym wierszu dodajesz dodatkową linię do obszaru wzorów, więc wygląda to tak:

^line 1s contents\nline 2s contents$

Następnie w pierwszej linii i każdej kolejnej linii - z wyjątkiem ostatniej - dodajesz kolejną linię do obszaru wzorów. Wygląda to tak:

^line 1\nline 2\nline 3$

Jeśli twój adres IP zostanie znaleziony w tobie, Print do pierwszej nowej linii, więc po prostu wiersz 1 tutaj. Pod koniec każdego cyklu robisz to Dsamo i zaczynasz od nowa. Następny cykl wygląda następująco:

^line 2\nline 3\nline 4$

...i tak dalej. Jeśli twoje IP ma być znalezione na którymś z tych trzech, najstarsze wydrukuje - za każdym razem. Więc zawsze masz tylko trzy linie do przodu.

Oto szybki przykład. Otrzymam trzywierszowy bufor dla każdej liczby kończącej się na zero:

seq 10 52 | sed '1N;$!N;/0\(\n\|$\)/P;D'

10
18
19
20
28
29
30
38
39
40
48
49
50

Ten jest trochę bardziej skomplikowany niż twój przypadek, ponieważ musiałem przełączyć się z 0\nnowej linii lub 0$końca obszaru wzorów, aby bardziej przypominać twój problem - ale są one nieco inne, ponieważ wymaga to zakotwiczenia - co może być trochę trudne, ponieważ przestrzeń wzorów stale się zmienia.

Użyłem nieparzystych przypadków 10 i 52, aby pokazać, że tak długo, jak kotwica jest elastyczna, tak samo jak wynik. W pełni przenośny, mogę osiągnąć te same wyniki, zamiast tego licząc na algorytm i wykonując:

seq 10 52 | sed '1N;$!N;/[90]\n/P;D'

I poszerz wyszukiwanie, ograniczając moje okno - od 0 do 9 i 0 oraz od 3 linii do dwóch.

W każdym razie masz pomysł.

mikeserv
źródło
Dzięki za całą ciężką pracę. Przepraszam, gdzie mam umieścić nazwę pliku, który chciałbym przeszukać?
Dale
@Dale - mój zły. sed '...' $filename. Nawiasem mówiąc - zostawiłem kropki z własnego ciągu wyszukiwania, ale tak naprawdę nie są to kropki we wzorze - reprezentują one dowolny pojedynczy znak. Powinieneś chyba oct\.oct\.oct\.octuciec, aby uciec od nich, aby pasowały tylko do kropek.
mikeserv
Próbowałem użyć tego i różnych symboli <>, ale nie udało mi się znaleźć zdarzenia, które otrzymuję z innymi rozwiązaniami tutaj, więc zastanawiam się, czy mój system operacyjny nie jest zgodny z tymi rozwiązaniami.
Dale
teraz wyniki z -> N; /141.299.99.1/P; D ': Nie znaleziono zdarzenia.
Dale
@Dale - zobacz aktualizację. To powinno ci pomóc.
mikeserv
4

Ponieważ wspominasz , że nie masz takiej -Bopcji grep, możesz użyć Perla (na przykład), aby przesunąć okno o 4 liniach:

perl -ne '
    push @window,$_;
    shift @window if @window > 4;
    print @window if /141\.299\.99\.1/
' your_file

Odpowiedź Ramesha robi podobnie awk.

Joseph R.
źródło
Nie jestem pewien, czy moja wersja Perla to obsługuje, ale spróbuję. Dziękuję bardzo za poświęcenie czasu na odpowiedź na moje pytanie - bardzo wdzięczne!
Dale
@Dale Nie ma za co. Wątpię, aby ten kod korzystał z najnowszych funkcji Perla.
Joseph R.
4

Jeśli jest dostępny, możesz użyć pcregrep :

pcregrep -M '.*\n.*\n.*\n141.299.99.1' file
chaos
źródło
Sprawdzanie, czy mam PCREGREP. Podoba mi się zwartość polecenia. Bardzo wdzięczny za poświęcony czas i wysiłek. Dziękuję Ci!!!
Dale
4

Możesz wdrożyć to samo podstawowe podejście, co inne odpowiedzi inne niż grep w samej powłoce (zakłada to stosunkowo nową powłokę, która obsługuje =~):

while IFS= read -r line; do 
    [[ $line =~ 141.299.99.1 ]] && printf "%s\n%s\n%s\n%s\n" $a $b $c $line;
    a=$b; b=$c; c=$line; 
done < file 

Alternatywnie, możesz umieścić cały plik w tablicy:

perl -e '@F=<>; 
        for($i=0;$i<=$#F;$i++){
          print $F[$i-3],$F[$i-2],$F[$i-1],$F[$i] if $F[$i]=~/141.299.99.1/
        }' file 
terdon
źródło
Moja skorupa jest bardzo stara - Steve Jobs Open Step. Świetny pomysł i dziękuję za poświęcony czas !!! Dale
Dale
@Dale the perl podejście będzie działać prawie wszędzie. Poinformuj nas o swoim systemie operacyjnym (dodaj go do swojego pytania), abyśmy mogli zaproponować Ci rzeczy, które będą dla Ciebie działać.
terdon
Jeśli skopiuję twój Perl i włożę go do NotePada i ustawię w jednym wierszu, to zadziała! Pytanie - jeśli chcę, powiedzmy 10 linii przed wzorcem dopasowania, gdzie miałbym zmienić 3 na 10? Dzięki!
Dale
Widzę, że mogę dodać więcej wierszy z powrotem, dodając więcej instrukcji $ F [$ iX]. Dzięki!
Dale
4

Jeśli twój system nie obsługuje grepkontekstu, możesz zamiast tego spróbować ack-grep :

ack -B 3 141.299.99.1 file

ack to narzędzie takie jak grep, zoptymalizowane dla programistów.

Cuonglm
źródło
Podoba mi się zwartość polecenia, ale mój system nie obsługuje potwierdzenia wyszukiwania na stronach podręcznika. Świetny pomysł i bardzo dziękuję za poświęcony czas !!! Dale
Dale
@Dale: Zaskakujące! Jaki jest twój system operacyjny? Jeśli masz perl, możesz użyć ack.
cuonglm
2
awk '/141.299.99.1/{for(i=1;i<=x;)print a[i++];print} {for(i=1;i<x;i++)
     a[i]=a[i+1];a[x]=$0;}'  x=3 filename

W tym awkrozwiązaniu używana jest tablica, która zawsze będzie zawierać 3 linie przed bieżącym wzorcem. Dlatego po dopasowaniu wzoru drukowana jest zawartość tablicy wraz z bieżącym wzorem.

Testowanie

-bash-3.2$ cat filename
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.11
10.0.0.12
10.0.0.13
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
10.0.0.17
10.0.0.18
10.0.0.19

Po wykonaniu polecenia wynik jest następujący:

10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
Ramesh
źródło
tak szczegółowe - dziękuję bardzo. Dam temu szansę. Bardzo wdzięczny za poświęcony czas !! Dale
Dale
Mam plik testowy i twoje rozwiązanie działa! Problemem jest to, że kiedy uruchamiam go na moim dużym pliku produkcyjnym, pojawia się on z zbyt długim numerem rekordu, więc dane wyjściowe nie są w stanie współpracować z poleceniem. Moje oryginalne polecenie u góry tej strony działa, ale znajduje tylko jedno wystąpienie. Doceniam twoją pomoc. Czy jest coś, co mogę zrobić z moim oryginalnym poleceniem, aby znaleźć więcej niż jedną instancję?
Dale
1

W większości z nich /141.299.99.1/będzie również pasować (np.) 141a299q99+1Lub 141029969951dlatego, że .w wyrażeniu regularnym może reprezentować dowolny znak.

Korzystanie /141[.]299[.]99[.]1/jest bezpieczniejsze i można dodać dodatkowy kontekst na początku i na końcu całego regexp, aby upewnić się, że nie pasuje 3141., .12, .104, itd.

użytkownik117529
źródło
1
To dobra uwaga - i to też rozważałem. Mimo to użyłem łańcucha dostarczonego przez pytającego jako znanego zapałkę roboczą - i powiadomiłem go osobiście o tym samym, gdy tylko nadarzy się okazja. W każdym razie - nie wszystkie - odpowiedź steeldrivera od samego początku zacytowała mecz char.
mikeserv