Grepの一致と抽出


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/httptcp/httpsudp/dns

これまでのところ、これを試しましたgrep -o 'proto=[^/]*/'が、値はとしてのみ抽出できましたproto=tcp/



これはsedawkまたは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
}

また、フィールド名でデータを参照するだけで、データを好きなように実行できます。たとえば-e、ファイル内のスクリプトとコマンドラインスクリプトを簡単に混合できるようにGNU awkを使用します。

$ 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を、ソートなどのような
エド・モートン

@EdMortonはtrueですが、Perlがデフォルトで含まれていないディストリビューションに個人的に遭遇したことはありません。コンプレックスawksedスクリプトは通常単純でperl、それは一般的なタスクの追加機能で、本質的にそれらのスーパーセットなので。
OrangeDog

@OrangeDogだれもs/old/new/g、sedがawkではなく、sedスクリプトよりも複雑なsedスクリプトを作成することはできません。Perlでは、複雑なawkスクリプトの方が単純であることにまったく同意しません。もちろん、簡潔にすることはできますが、ソフトウェアの簡潔さは望ましい属性ではありません。簡潔さは重要です。実際にメリットがあることは非常にまれであり、通常、読むのがはるかに難しいため、zoitz.comなどの記事を投稿します。 / archives / 13 perlについて、awkとは異なり、書き込み専用言語として参照します。私はまだこれに相当するperlを見たいと思っています
Ed Morton

13

ではgrep -o、抽出したいものと正確に一致させる必要があります。proto=文字列を抽出したくないので、一致させないでください。

スラッシュと空でない英数字の文字列のいずれかと一致するかtcpudpその後に続く拡張正規表現は、次のとおりです。

(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、デフォルトのフィールドセパレータを使用して、最初のフィールドを分割し、=その2番目のビットを出力します。

$ 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=ようにが部分文字列である照合フィールドからも保護する場合は、次のようにしてthisisnotaproto=tcp/https単語境界を追加できます\b

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

1
あなたはただ書くことでそれを改善することができgrep -oP 'proto=\K\S+'ます。のproto=tcp/http後にスペースの代わりにタブが続く場合があり、非スペース文字と\Sは異なり[^ ]ます。
モスビー

@mosvy:それは良い提案です、ありがとう。
user000001

1
とにかく、-oGNUismも同様です。PCREサポートを使用してビルドされた場合-Pのみ、GNUによってサポートされますgrep(ビルド時にオプション)。
ステファンChazelas

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、「非空白」のために+「1つ以上」およびキャプチャするための括弧のために。最後に、最後の/psedは、操作が成功した場合にのみsedに行を出力させます。つまり、置換演算子に一致した場合です。

そして、perl one:

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

-n「入力ファイル-eを1行ずつ読み取り、指定されたスクリプトを各行に適用する」という意味です。-lそれぞれの改行追加printコールを(入力から出射改行を除去します)。スクリプト自体は、の後に見つかった空白以外の文字の最も長いストレッチを印刷しproto=ます。


1
-Eますますポータブルになっ\Sてきていますが、そうではありません。[^[:space:]]よりポータブルな同等物です。
ステファンChazelas

1

これは非常に簡単な別のソリューションです。

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

あなたgrepは何にもマッチしません。[tc,ud]\*\\/.*探し1件のいずれかの発生t、またはc、または,またはuまたはdリテラルに続いて、*その後、文字pやバックスラッシュ。たぶんgrep -Eo '(tc|ud)p/.* ' file | awk '{print $1}'。ただし、awkを使用している場合は、awkですべてを実行することもできますawk -F'[= ]' '/(tc|ud)p/{print $2}' file
terdon

誰かが私のオリジナルを変更しました、スターの前に余分なバックスラッシュがありました、私はちょうどサーを削除しました。
mkzia

編集ありがとうございます。残念ながらうまくいきません。私は前に説明したように、[tc,ud]p手段「の一つはtc,uまたはdに続くp理由だけで、それはここに一致するように。tcp持っているcpudp持っているdp。しかし、それはまた、マッチする,ptpなどは、また、今あなたが持っていること*、それが一致するppp(だけでなく*手段「0以上」)が一致しない場合でも、それが一致するようにするには、(文字クラスを望んでいない。[ ])、何がしたいことはグループである:(tc|ud)との(使用-Eのフラグgrep。)また、.*これます行全体に一致
terdon

1
@Jesse_b:mkziaは技術的には「新しい貢献者」ではありませんが、コマンドにコード形式を使用しなかったという事実から明らかなように、経験の浅いユーザーです。それでも、コマンド\*の最初の*文字がイタリックのマークダウンではなく*として表示されるように入力するのに十分なほどスマートでした。コマンドをコード形式にすると、の\*が表示されます(そのため、コマンドが失敗します)。他の人の投稿を編集するときは、このように投稿の外観を変更しないように注意してください。
G-Manは 'Reinstate Monica'

@terdon:(1)いいえ、実際には一致しませんppp。もちろんあなたはそれが一致する右のことだ,pか  tp-あるいはuucpttpcutpductpまたはd,up
G-Manは 'Reinstate Monica'


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.