Grep Dopasuj i wyodrębnij


10

Mam plik zawierający linie jako

proto=tcp/http  sent=144        rcvd=52 spkt=3 
proto=tcp/https  sent=145        rcvd=52 spkt=3
proto=udp/dns  sent=144        rcvd=52 spkt=3

I trzeba wyodrębnić wartość proto który jest tcp/http, tcp/https, udp/dns.

Do tej pory próbowałem tego, grep -o 'proto=[^/]*/'ale tylko w stanie wyodrębnić wartość jako proto=tcp/.



To jest zadanie dla sed, awklub perlnie grep.
— OrangeDog,

Odpowiedzi:


1

Zakładając, że jest to związane z twoim poprzednim pytaniem , idziesz niewłaściwą ścieżką. Zamiast próbować poskładać fragmenty skryptów, które w pewien sposób będą robić to, co chcesz przez większość czasu i musisz uzyskać zupełnie inny skrypt za każdym razem, gdy będziesz musiał zrobić coś choć trochę innego, po prostu stwórz 1 skrypt, który może przeanalizować twój plik wejściowy do tablicy ( f[]poniżej), która odwzorowuje nazwy pól (tagi) na ich wartości, a następnie możesz zrobić, co chcesz z wynikiem, np. biorąc pod uwagę ten plik wejściowy z poprzedniego pytania:

$ cat file
Feb             3       0:18:51 17.1.1.1                      id=firewall     sn=qasasdasd "time=""2018-02-03"     22:47:55        "UTC""" fw=111.111.111.111       pri=6    c=2644        m=88    "msg=""Connection"      "Opened"""      app=2   n=2437       src=12.1.1.11:49894:X0       dst=4.2.2.2:53:X1       dstMac=42:16:1b:af:8e:e1        proto=udp/dns   sent=83 "rule=""5"      "(LAN->WAN)"""

możemy napisać skrypt awk, który tworzy tablicę wartości indeksowanych według ich nazw / znaczników:

$ cat tst.awk
{
    f["hdDate"] = $1 " " $2
    f["hdTime"] = $3
    f["hdIp"]   = $4
    sub(/^([^[:space:]]+[[:space:]]+){4}/,"")

    while ( match($0,/[^[:space:]]+="?/) ) {
        if ( tag != "" ) {
            val = substr($0,1,RSTART-1)
            gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
            f[tag] = val
        }

        tag = substr($0,RSTART,RLENGTH-1)
        gsub(/^"|="?$/,"",tag)

        $0 = substr($0,RSTART+RLENGTH)
    }

    val = $0
    gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
    f[tag] = val
}

i biorąc pod uwagę, że możesz robić, co chcesz z danymi, po prostu odwołuj się do nich za pomocą nazw pól, np. używając GNU awk -edla ułatwienia mieszania skryptu w pliku ze skryptem wiersza poleceń:

$ awk -f tst.awk -e '{for (tag in f) printf "f[%s]=%s\n", tag, f[tag]}' file
f[fw]=111.111.111.111
f[dst]=4.2.2.2:53:X1
f[sn]=qasasdasd
f[hdTime]=0:18:51
f[sent]=83
f[m]=88
f[hdDate]=Feb 3
f[n]=2437
f[app]=2
f[hdIp]=17.1.1.1
f[src]=12.1.1.11:49894:X0
f[c]=2644
f[dstMac]=42:16:1b:af:8e:e1
f[msg]="Connection"      "Opened"
f[rule]="5"      "(LAN->WAN)"
f[proto]=udp/dns
f[id]=firewall
f[time]="2018-02-03"     22:47:55        "UTC"
f[pri]=6

$ awk -f tst.awk -e '{print f["proto"]}' file
udp/dns

$ awk -f tst.awk -e 'f["proto"] ~ /udp/ {print f["sent"], f["src"]}' file
83 12.1.1.11:49894:X0

2
To jest niesamowite, dziękuję bardzo :)
— user356831

Do tego rodzaju pracy perlmoże być łatwiejszy w użyciu.
— OrangeDog,

1
@OrangeDog, dlaczego tak myślisz? Chciałbym zobaczyć odpowiednik w perlu, jeśli nie miałbyś nic przeciwko opublikowaniu takiej odpowiedzi. Perla zdecydowanie nie będzie łatwiejszy w użyciu, jeśli nie mam go na swoim pudełku i nie mogę go zainstalować, z czym często miałem do czynienia przez lata. Z drugiej strony Awk jest obowiązkowym narzędziem, dlatego jest zawsze obecne w instalacjach UNIX, podobnie jak sed, grep, sort itp.
— Ed Morton

@EdMorton prawda, chociaż nigdy osobiście nie spotkałem dystrybucji, w której perl nie był domyślnie dołączony. Skomplikowane awki sedskrypty są zwykle prostsze, perlponieważ jest to w zasadzie ich nadzbiór, z dodatkowymi funkcjami do typowych zadań.
— OrangeDog,

@OrangeDog nikt nigdy nie powinien pisać skryptu sed, który jest bardziej skomplikowany, s/old/new/ga sed nie jest awk, więc odłóżmy to na bok. Całkowicie się nie zgadzam, że skomplikowane skrypty awk są prostsze w perlu. Oczywiście mogą być krótsze, ale zwięzłość nie jest pożądanym atrybutem oprogramowania, jest to zwięzłe i niezwykle rzadko mają one jakąkolwiek rzeczywistą korzyść, a ponadto są znacznie trudniejsze do odczytania, dlatego ludzie publikują takie rzeczy jak zoitz.com / archives / 13 o perlu i nazywają go językiem tylko do zapisu, w przeciwieństwie do awk. Nadal chciałbym zobaczyć perla równoważnego temu
— Ed Morton

13

Za pomocą grep -omusisz dopasować dokładnie to, co chcesz wyodrębnić. Ponieważ nie chcesz wyodrębniać proto=ciągu, nie powinieneś go dopasowywać.

Rozszerzone wyrażenie regularne, które pasowałoby do jednego tcplub udppo którym następuje ukośnik i niepusty ciąg alfanumeryczny to

(tcp|udp)/[[:alnum:]]+

Zastosowanie tego do danych:

$ grep -E -o '(tcp|udp)/[[:alnum:]]+' file
tcp/http
tcp/https
udp/dns

Aby upewnić się, że robimy to tylko w wierszach rozpoczynających się od ciągu proto=:

grep '^proto=' file | grep -E -o '(tcp|udp)/[[:alnum:]]+'

Z sed, usuwając wszystko przed pierwszym =i po pierwszym pustym znaku:

$ sed 's/^[^=]*=//; s/[[:blank:]].*//' file
tcp/http
tcp/https
udp/dns

Aby mieć pewność, że robimy to tylko w wierszach rozpoczynających się od łańcucha proto=, możesz wstawić ten sam krok wstępnego przetwarzania grepjak powyżej lub możesz użyć

sed -n '/^proto=/{ s/^[^=]*=//; s/[[:blank:]].*//; p; }' file

Tutaj pomijamy domyślne wyjście z -nopcją, a następnie uruchamiamy podstawienia i wyraźny wydruk linii tylko wtedy, gdy linia pasuje ^proto=.


Za awkpomocą domyślnego separatora pól, a następnie podzielenie pierwszego pola =i wydrukowanie jego drugiego bitu:

$ awk '{ split($1, a, "="); print a[2] }' file
tcp/http
tcp/https
udp/dns

Aby mieć pewność, że robimy to tylko w wierszach rozpoczynających się od łańcucha proto=, możesz wstawić ten sam krok wstępnego przetwarzania grepjak powyżej lub możesz użyć

awk '/^proto=/ { split($1, a, "="); print a[2] }' file

10

Jeśli korzystasz z GNU grep (dla -Popcji), możesz użyć:

$ grep -oP 'proto=\K[^ ]*' file
tcp/http
tcp/https
udp/dns

Tutaj dopasowujemy proto=ciąg, aby upewnić się, że wyodrębniamy poprawną kolumnę, ale następnie odrzucamy ją z wyniku za pomocą \Kflagi.

Powyższe zakłada, że ​​kolumny są rozdzielone spacjami. Jeśli tabulatory są również prawidłowym separatorem, użyłbyś \Sdo dopasowania znaków spacji, więc komenda wyglądałaby następująco:

grep -oP 'proto=\K\S*' file

Jeśli chcesz również chronić przed polami dopasowania, w których proto=znajduje się podłańcuch, taki jak a thisisnotaproto=tcp/https, możesz dodać granicę słów za pomocą \b:

grep -oP '\bproto=\K\S*' file

1
Możesz to poprawić, pisząc tylko grep -oP 'proto=\K\S+'. Po proto=tcp/httpspacji może występować tabulator zamiast spacji, w \Sprzeciwieństwie do [^ ]pasujących do znaków spacji.
— mosvy

@mosvy: To dobra sugestia, dzięki.
— user000001

1
W każdym razie -ojest to również GNUism. -Pjest obsługiwany tylko przez GNU, grepjeśli jest zbudowany z obsługą PCRE (opcjonalnie w czasie kompilacji).
— Stéphane Chazelas

6

Używanie awk:

awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input

$1 ~ "proto"upewnimy się, że podejmujemy działania tylko na wierszach z protopierwszej kolumny

sub(/proto=/, "")usunie proto=z wejścia

print $1 wypisuje pozostałą kolumnę


$ awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input
tcp/http
tcp/https
udp/dns

3

Kodowanie golfa w greprozwiązaniach

grep -Po "..p/[^ ]+" file

lub nawet

grep -Po "..p/\S+" file

3

Za pomocą cutpolecenia:

cut -b 7-15 foo.txt

3
Obejmuje to końcowe spacje w liniach httpi dns.
— G-Man,

2

Po prostu inne greprozwiązanie:

grep -o '[^=/]\+/[^ ]\+' file

I podobny z seddrukowaniem tylko dopasowanej przechwyconej grupy:

sed -n 's/.*=\([^/]\+\/[^ ]\+\).*/\1/p' file

1

Inne awkpodejście:

$ awk -F'[= ]' '/=(tc|ud)p/{print $2}' file
tcp/http
tcp/https
udp/dns

Spowoduje to ustawienie separatora pól awk na jeden =lub spację. Następnie, jeśli linia pasuje do a =, następnie albo udalbo tcpo nim p, wydrukuj drugie pole.

Innym sedpodejściem (nie przenośny do wszystkich wersji sed, ale współpracuje z GNU sed):

$ sed -En 's/^proto=(\S+).*/\1/p' file 
tcp/http
tcp/https
udp/dns

Te -nśrodki „nie drukować” i -Eumożliwia rozszerzonych wyrażeń regularnych, które dają nam \Sdo „nie-białych znaków” +na „jeden lub więcej” i nawiasów do przechwytywania. Wreszcie, /pna końcu sprawi, że sed wydrukuje linię tylko wtedy, gdy operacja zakończyła się powodzeniem, więc jeśli wystąpiło dopasowanie dla operatora podstawienia.

I perlowy:

$ perl -nle '/^proto=(\S+)/ && print $1' file 
tcp/http
tcp/https
udp/dns

Te -nśrodki „czytać wiersz po wierszu pliku wejściowego i zastosować skrypt podany przez -ekażdej linii”. -lDodaje się do każdej nowej linii printpołączenia (i usuwa nowe linie wychodzące z wejścia). Sam skrypt wypisze najdłuższy ciąg znaków niebiałych spacji znalezionych po proto=.


1
-Estaje się coraz bardziej przenośny, ale \Snie jest. [^[:space:]]jest bardziej przenośnym odpowiednikiem.
— Stéphane Chazelas

1

Oto inne rozwiązanie dość proste:

grep -o "[tc,ud]*p\\/.*  "   INPUTFile.txt  |   awk '{print $1}'

Twoje grepnic nie pasuje. [tc,ud]\*\\/.*szuka jednego wystąpienia t, lub c, lub ,lub ulub d, po którym następuje dosłowny *znak, a następnie a pi odwrotny ukośnik. Prawdopodobnie miałeś na myśli grep -Eo '(tc|ud)p/.* ' file | awk '{print $1}'. Ale wtedy, jeśli używasz awk, można również zrobić całość w awk: awk -F'[= ]' '/(tc|ud)p/{print $2}' file.
— terdon

Ktoś zmodyfikował mój oryginał, przed gwiazdą pojawił się dodatkowy ukośnik, który właśnie usunąłem Sir.
— mkzia

Dzięki za edycję, ale obawiam się, że to działa tylko przez przypadek. Jak wyjaśniono wcześniej, [tc,ud]poznacza „jeden t, c, ,, ulub dpo przez p. Więc pasuje tu tylko dlatego, że tcpma cpi udpma dp. Ale to też pasuje ,plub tpitd. Również teraz, że masz *, to pasuje pppjak dobrze (the *. oznacza „0 lub więcej”, więc będzie on pasował nawet gdy nie pasuje) nie chcesz klasę postaci ( [ ]), co chcesz to grupa: (tc|ud)(korzystanie z -Eflagą grep). Ponadto, .*czyni go dopasuj całą linię
— terdon

1
@Jesse_b: Chociaż mkzia nie jest technicznie „nowym współtwórcą”, jest niedoświadczonym użytkownikiem, o czym świadczy fakt, że nie użyli formatowania kodu do swoich poleceń. A jednak byli na tyle sprytni, aby pisać, \*aby pierwszy *w swoim poleceniu pojawił się jako *, a nie kursywą. Po umieszczeniu polecenia w formacie kodu spowodowałeś \pojawienie się przed nim *(co spowodowało niepowodzenie polecenia). Gdy edytujesz posty innych osób, uważaj na zmianę wyglądu posta w ten sposób.
— G-Man

@terdon: (1) Nie, właściwie to nie pasuje ppp. Oczywiście masz rację, że będzie on pasował ,palbo  tp- albo uucp, ttp, cutp, ductplub d,up.
— G-Man


0
cat file| cut -f1 -d' '| cut -f2 -d'='
tcp/http
tcp/https
udp/dns

opcje cięcia:

  • -f - pole
  • -d - delimetr
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.