ほっしーの技術ネタ備忘録

技術ネタの備忘録です。基本的に私が忘れないためのものです。他の人の役にも立つといいなぁ。

grep と sed を一度にやる

ログとかコマンドの出力から、該当行だけ抜き出して sed で編集する。 みたいなことが必要になることがあります。

無理やりな例だと、jail の etc 以下を比較してみましょう。

$ sudo diff -rq /usr/jails/newjail/etc /usr/jails/m/etc | grep "^Only in"

片方にしかないファイルの一覧を出します。

これと同じことが sed でもできます。

$ sudo diff -rq /usr/jails/newjail/etc /usr/jails/m/etc | sed -n -e '/^Only in / p'

-n は各行の出力を原則無しにする引数です。そうすると何も出力されなくなるので、 正規表現の末尾に p コマンドを追加して、ヒットした行だけ表示します。

この辺の文法は awk に似ていて、-e の式は 条件文 + コマンド の形に本来はなります。 /^Only in/正規表現のマッチングで、これがヒットした場合に、p コマンド(出力)を実行します。

grep と同じことができるなら grep でいいじゃん?という話でもありますが、 まず grepsed は採用している正規表現の文法が微妙に違うので、 sed にしかない表記をしたい場合に役立ちます。

また、sed-e は複数連続して記述できるので、

$ sudo diff -rq /usr/jails/newjail/etc /usr/jails/m/etc | sed -n -e 's/: /\//' -e '/^Only in / p'

こんな風に、区切り文字を置き換えてから検索、みたいなのが sed の 1 プロセスで終わります。 つまり少しだけコンピュータに優しい命令になります。

さらに、正規表現の match ではなく、置換も条件文として使えるので、

$ sudo diff -rq /usr/jails/newjail/etc /usr/jails/m/etc | sed -n -e 's/: /\//' -e 's/^Only in // p'

こうやって、Only in を削除してから出力。みたいなこともできます。

複数の -e はそれぞれ1つのコマンドなので、; で区切って複数命令渡すこともできます。

$ sudo diff -rq /usr/jails/newjail/etc /usr/jails/m/etc | sed -n -e 's/: /\//; s/^Only in // p'

ここで、: を置き換える命令はそれ単独で動く命令なので、 Only in にマッチしようがしまいが置換されますし、 : にマッチしなくても、Only in にマッチすれば出力対象となります。

シェル曲芸的な感じだと、こんなこともできます。

$ cat maillog | sed -n -e '/postfix\/smtpd/ s/^.*connect from \(.*\)\[.*$/\1/p' -e '/ pop3-login: / s/^.*user=<\([^>]*\)>.*$/\1/p'

smtpd のログなら接続元を表示し、pop3 でのログインならユーザ ID を表示する。 grep を別で実行すると複数命令に分かれますし、時系列順が壊れてしまうので、 まぁこういうのが必要になることは……なさそう?