おおよそ次のログファイルがあります。
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/instance0
core 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