Греп сірник і витяг


10

У мене є файл, який містить рядки як

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

Мені потрібно витягти значення прото яке tcp/http, tcp/https, udp/dns.

Поки я намагався це зробити, grep -o 'proto=[^/]*/'але тільки міг отримати значення як proto=tcp/.



Це робота для sed, awkчи perlні grep.
OrangeDog

Відповіді:


1

Якщо припустити, що це пов’язано з вашим попереднім запитанням , ви йдете неправильним шляхом. Замість того, щоб намагатися скласти шматочки сценаріїв, які зроблять своєрідне / сортування робити те, що ви хочете більшу частину часу, і вам потрібно буде отримувати зовсім інший сценарій кожного разу, коли вам потрібно зробити що-небудь найменше трохи інше, просто створіть 1 сценарій, який може розібрати ваш вхідний файл в масив ( f[]нижче), який відображає назви ваших полів (тегів) на їх значення, і тоді ви можете робити все, що завгодно, з результатом, наприклад, враховуючи цей вхідний файл з попереднього питання:

$ 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)"""

ми можемо написати сценарій awk, який створює масив значень, індексованих їх іменами / тегами:

$ 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
}

і з огляду на те, що ви можете робити все, що завгодно, зі своїми даними, просто посилаючи їх на назви полів, наприклад, використовуючи GNU awk -eдля зручності змішування сценарію у файлі зі скриптом командного рядка:

$ 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
Це приголомшливо, дуже дякую :)
user356831

Для такої роботи perlможе бути простіше у використанні.
OrangeDog

1
@OrangeDog чому ти вважаєш це? Я насправді хотів би побачити еквівалент в perl, якщо ви не проти опублікувати таку відповідь. Perl, безумовно, не буде простішим у використанні, якщо я не маю його на коробці і не можу встановити його, проте це те, з чим часто доводилося стикатися протягом багатьох років. З іншого боку, Awk - це обов'язкова утиліта, і тому вона завжди присутня на установках UNIX, як і sed, grep, sort тощо.
Ед Мортон,

@EdMorton вірно, хоча я ніколи особисто не стикався з дистрибуцією, де perl не був включений за замовчуванням. Складні awkта sedсценарії, як правило, простіші, perlоскільки вони, по суті, їх сукупність, з додатковими функціями для загальних завдань.
OrangeDog

@OrangeDog ніхто ніколи не повинен писати сценарій sed, який складніший ніж, s/old/new/gі sed не awk, тому дозволяє відкласти це. Я абсолютно не погоджуюся з тим, що складні сценарії awk простіші в перл. Вони, звичайно, можуть бути більш короткими, але стислість не є бажаним атрибутом програмного забезпечення, стислість є, і вкрай рідко для них є якась реальна користь, а також читати їх набагато складніше, тому люди публікують такі речі, як zoitz.com / archives / 13 про perl і посилаються на нього як на мову лише для запису, на відміну від awk. Я все одно хотів би побачити
Ед Мортон

13

З grep -o, вам доведеться відповідати саме тому, що ви хочете витягти. Оскільки ви не хочете витягувати proto=рядок, вам не слід відповідати.

Розширений регулярний вираз, який би відповідав tcpабо udpслідом за косою рисою, і деякою непустою буквено-цифровою строкою

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

Застосовуючи це у своїх даних:

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

Щоб переконатися, що ми робимо це лише в рядках, що починаються з рядка proto=:

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

З sed, видаляючи все до першого =та після першого порожнього символу:

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

Щоб переконатися, що ми робимо це лише в рядках, що починаються з рядка proto= , ви можете вставити той же крок попередньої обробки, що grepі вище, або ви можете використовувати

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

Тут ми придушуємо вихідний -nпараметр за замовчуванням за допомогою параметра, а потім запускаємо підстановки та явний друк рядка, лише якщо рядок збігається ^proto=.


З awk, використовуючи роздільник поля за замовчуванням, а потім розділити перше поле на= друк і друкуючи другий біт:

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

Щоб переконатися, що ми робимо це лише в рядках, що починаються з рядкаproto= , ви можете вставити той же крок попередньої обробки, що grepі вище, або ви можете використовувати

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

10

Якщо ви перебуваєте на GNU grep (для -Pопції), ви можете використовувати:

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

Тут ми співставляємо proto=рядок, щоб переконатися, що ми дістаємо правильний стовпець, але потім відкидаємо його з виводу з\K прапором.

Вищенаведене передбачає, що стовпці розділені пробілом. Якщо вкладки також є дійсним роздільником, ви використовуєте \Sдля узгодження символів, що не містять пробілів, так що команда буде:

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

Якщо ви також хочете захистити від полів відповідності, де proto=знаходиться підрядка, наприклад, a thisisnotaproto=tcp/https, ви можете додати межу слова з \bтаким чином:

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

1
Ви можете покращити це, написавши просто grep -oP 'proto=\K\S+'. За ними proto=tcp/httpможе йти вкладка замість пробілів, і \Sна відміну від [^ ]них, відповідатиме будь-який непробільний символ.
mosvy

@mosvy: Це гарна пропозиція, дякую.
користувач000001

1
У будь-якому випадку, -oце також GNUism. -Pпідтримується GNU лише в тому grepвипадку, якщо він створений за допомогою підтримки PCRE (необов'язково під час збирання).
Стефан Шазелас

6

Використання awk:

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

$1 ~ "proto"буде гарантувати, що ми вживатимемо лише рядків із protoпершого стовпця

sub(/proto=/, "") видалить proto= із входу

print $1 друкує решту стовпців


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

3

Код гольфу на grepрішеннях

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

або навіть

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


2

Ще одне grepрішення:

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

І схожий з sedдруком лише відповідних захоплених груп:

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

1

Інший awkпідхід:

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

Це встановить роздільник поля awk на =або пробіл. Тоді, якщо рядок відповідає а =, то udабоtc супроводжуваної p, друку 2 - е поля.

Інший sedпідхід (не портативний для всіх версій sed, але працює з GNU sed):

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

В -nозначає «не друкувати» і -Eдозволяє розширені регулярні вирази , які дають нам \Sдля «непробельний», +для «один або більше» , а в дужках для захоплення. Нарешті,/p в кінці змусить sed надрукувати рядок тільки в тому випадку, якщо операція пройшла успішно, тому якщо була відповідність оператору заміни.

І, перл:

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

Засіб -n"прочитати вхідний файл рядок за рядком і застосувати сценарій, заданий -eкожним рядком". -lДодає символ нового рядка до кожного printвиклику (і видаляє залишають перекладу рядки з вхідного). Сам сценарій надрукує найдовший розріз символів, що не містять пробілів, знайдені після proto=.


1
-Eстає все більш портативним, але \Sце не так. [^[:space:]]є більш портативним еквівалентом.
Стефан Шазелас

1

Ось ще одне досить просте рішення:

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

Ваш grepнічого не відповідає. [tc,ud]\*\\/.*виглядає для одного входження або t, або c, або , ,або , uабо d, а потім буквального *характер, то pзворотна коса риска. Ви , ймовірно , мав в виду grep -Eo '(tc|ud)p/.* ' file | awk '{print $1}'. Але тоді, якщо ви використовуєте awk, ви також можете зробити всю справу у awk : awk -F'[= ]' '/(tc|ud)p/{print $2}' file.
тердон

Хтось змінив мій оригінал, перед зіркою з’явився додатковий зворотний ривок, який я щойно видалив.
mkzia

Дякую за редагування, але боюся, що працює лише випадково. Як я вже пояснював раніше, [tc,ud]pозначає «один з t, c, ,, uабо dсупроводжуваний p. Так що це відповідає тут тільки тому , що tcpє cpі udpє dp. Але це буде також відповідати ,pабо й tpт.д. Крім того , тепер, коли у вас є *, це буде відповідати , pppа також ( *означає "0 або більше", тому він буде відповідати навіть тоді, коли він не збігається). Ви не хочете, щоб клас символів ( [ ]), те, що ви хочете, це група: (tc|ud)(використовувати з -Eпрапором grep). Крім того, .*робить це сполучати всю лінію.
terdon

1
@Jesse_b: Хоча mkzia технічно не є «Новим дописувачем», вони є недосвідченим користувачем, про що свідчить той факт, що вони не використовували форматування коду для своєї команди. І все-таки вони були досить розумні, щоб набрати, \*щоб перші *в їх команді з'явилися як *, а не як позначка курсивом. Коли ви ввели команду у формат коду, ви викликали появу \до цього *(таким чином, викликаючи збій команди). Редагуючи повідомлення інших людей, будь ласка, стежте за зміною зовнішності такої публікації.
G-Man каже: "Відновіть Моніку"

@terdon: (1) Ні, насправді це не збігатиметься ppp. Звичайно , ви маєте рацію , що це буде відповідати ,pабо  tp- або uucp, ttp, cutp, ductpабо d,up.
G-Man каже: "Відновіть Моніку"


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.