awkはsedのパイプ出力を処理できません。

awkはsedのパイプ出力を処理できません。

おおよそ次のログファイルがあります。

Sep 23 10:28:26 node kernel: em0: device is going DOWN
Sep 23 10:28:26 node kernel: em0: device is going UP
Sep 23 10:29:14 node cdsmon: /tmp/instance0 ; core dumped
Sep 23 10:29:14 node cdsmon: /tmp/instance0 ; core dumped
Sep 23 10:28:26 node kernel: em0: device is going DOWN
Sep 23 10:29:14 node cdsmon: /tmp/instance1 ; core dumped
Sep 23 10:28:26 node kernel: em0: device is going UP
Sep 23 10:29:14 node cdsmon: /tmp/instance2 ; core dumped

cdsmon行を検出してから行を分割したい(同様のイベントを;取得するため)/tmp/instance0core dumped

これを行うには、以下を使用しますsed

sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log

出力は次のとおりです。

/tmp/instance0 ; core dumped
/tmp/instance0 ; core dumped
/tmp/instance1 ; core dumped
/tmp/instance2 ; core dumped

ただし、この出力をawk以下のように配管すると、上記と同じ出力が得られます。

sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print $1}"

-uオプションを削除したにもかかわらず、同じ状況が観察されましたsed

私が何かを見逃しているなら、誰かがそれを指摘することができますか?私は通常のawk / sedでFreeBSDボックスを使用していますが、残念ながら新しいパッケージをインストールすることはできません。

答え1

この動作の理由は、awkプログラムが次に含まれているためです。ダブル引用符は、文字列をシェルの変数拡張に公開するようにします。これは、プログラムを実行するシェルが最初に展開され、$1それが定義されていない可能性があるため、空の文字列に展開されることを意味します。

したがって、あなたのプログラムは次のようになります。

awk -F ";" "{print}"

これがライン全体が印刷される理由です。これは常にawk(と)プログラムですsed一つ引用符。

sedほとんどの場合、出力をパイプに接続したり、awkその逆にパイプする必要はありません。あなたの例では、「イベントタグ」の後の最初のフィールドを取得するには、次のようにします。

sed -E -n 's/^.*cdsmon: ([^;]*).*$/\1/p' /tmp/dev.log 

これは以下を定義します。キャプチャグループcdsmon:最初の文字列の後に文字列を囲み、行;全体をそのキャプチャグループの内容に置き換えます。

記録されたイベントの概要を印刷するには、上記の方法を次のように拡張cdsmonできます。sed

sed -E -n 's/^.*cdsmon: ([^;]*) ; (.*)$/\1 : \2/p' dev.log 

または別のawk唯一のアプローチがあります。

awk -F'(cdsmon: | ; )' 'NF==3{printf "%s : %s\n",$2,$3}' dev.log 

あなたの例は、両方印刷されます

/tmp/instance0 : core dumped
/tmp/instance0 : core dumped
/tmp/instance1 : core dumped
/tmp/instance2 : core dumped

ただし、このawk方法では極端なケースが発生する可能性があります。パターンcdsmon:;フィールドの区切り記号を使用してください。 3つのフィールドがある場合(例では、エントリでのみ発生する場合cdsmon:)、afterインスタンス名cdsmon:とafter理由に対応する2番目と3番目のフィールドを印刷します;

答え2

awk手術を通して使用します。ここではコロンに分割するので、14 node cdsmon日付/時刻を考慮した後、ホストの一致を3番目のフィールド(たとえば)に適用する必要があります。

awk -F: '
    $3 ~ / cdsmon$/ {
        split($4, text, / *; */);    # Split field at semicolon
        sub(/^ */, "", text[1]);     # Remove leading space
        printf "instance %s, reason %s\n", text[1], text[2]
    }
' /tmp/dev.log

これは、以下で提案されている代替的で簡単な解決策です。コメント、コロンまたはセミコロンに分割するため、必要なフィールドはすでにawk変数に直接存在します。

awk -F': | *; *' '
    $1 ~ / cdsmon$/ { printf "instance %s, reason %s\n", $2, $3 }
' /tmp/dev.log

インスタンスを抽出する方法と理由を説明していないので(またはそうであれば逃した)、ただ文字列として印刷して正しく抽出されたことを証明しました。

答え3

指示によると:

二重引用符は、開始価格と終了価格の間のほとんどのすべてを保護します。シェルは、引用符付きテキストに対して少なくとも変数とコマンドの置換を実行します。他のシェルは、二重引用符で囲まれたテキストに対してさまざまな種類の処理を実行できます。

二重引用符で囲まれたテキストの一部の文字はシェルで処理されるため、テキスト内でエスケープする必要があります。注目すべき文字は '$'、 '''、 '\' および '''' で、文字通りプログラムに渡すには二重引用符で囲まれたテキストでこれらすべての前にバックスラッシュがなければなりません。

したがって、あなたの場合はドル記号を避けることができます$

sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print \$1}"

ただし、単一引用符を使用する方が簡単です。

sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ' ; ' '{ print $1 }'

' ; '各行の後に見えないスペースがないように、区切り文字の間にスペースを入れることもできます。

以下も使用できますawk

$ awk -F': | ; ' '/cdsmon/ { print $2 }' /tmp/dev.log
/tmp/instance0
/tmp/instance0
/tmp/instance1
/tmp/instance2

答え4

awk '{for(i=1;i<=NF;i++){if($i ~ /cdsmon/){print $(i+1),$(i+3),$(i+4)}}}' filename

出力

/tmp/instance0 core dumped
/tmp/instance0 core dumped
/tmp/instance1 core dumped
/tmp/instance2 core dumped





awk '{for(i=1;i<=NF;i++){if($i ~ /cdsmon/){print $(i+1)}}}' filename
/tmp/instance0
/tmp/instance0
/tmp/instance1
/tmp/instance2

関連情報