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.