回答:
簡単な方法はawkです。
tail -f /path/to/serverLog | awk '
/Printer is on fire!/ { system("shutdown -h now") }
/new USB high speed/ { system("echo \"New USB\" | mail admin") }'
はい、両方ともカーネルログからの実際のメッセージです。Perlは、このために使用する方が少しエレガントかもしれませんし、tailの必要性を置き換えることもできます。perlを使用している場合、次のようになります。
open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
if(eof $fd) {
sleep 1;
$fd->clearerr;
next;
}
my $line = <$fd>;
chomp($line);
if($line =~ /Printer is on fire!/) {
system("shutdown -h now");
} elsif($line =~ /new USB high speed/) {
system("echo \"New USB\" | mail admin");
}
}
awk
短くて簡単にオンザフライで1行で実行できるソリューションが気に入っています。しかし、実行したいコマンドに引用符がある場合、これは少し面倒になる可能性があります、代替手段があります、パイプラインと複合コマンドを使用して、簡単なワンライナーソリューションも可能ですが、結果のコマンドを渡す必要はありません文字列として?
system()
コマンド内で使用できるように解放されます。
tail -f /path/to/serverLog | grep "server is up" | head -1 && do_some_command
tail -n 0 -f /path/to/serverLog
、ファイルの最後の0行を読み取り、さらに行が印刷されるのを待つことです。
1つの可能性だけを探していて、awk
またはを使用するのではなく、ほとんどシェルに滞在したい場合は、perl
次のようなことができます。
tail -F /path/to/serverLog |
grep --line-buffered 'server is up' |
while read ; do my_command ; done
... my_command
「server is up」がログファイルに表示されるたびに実行されます。複数の可能性のために、あなたは多分落とすことができgrep
、代わりに使用case
範囲内while
。
大文字-F
はtail
、ログファイルがローテーションされるのを監視するように指示しています。つまり、現在のファイルの名前が変更され、同じ名前の別のファイルが代わりに使用tail
された場合、新しいファイルに切り替わります。
この--line-buffered
オプションはgrep
、各行の後にバッファーをフラッシュするように指示します。そうしmy_command
ないと、タイムリーに到達できない場合があります(ログのサイズが適切なサイズであると想定)。
--line-buffered
オプションを追加する必要があると思うかgrep
、そうでない場合は行間で出力をフラッシュする必要my_command
があります。必要に応じack
て、--flush
フラグがあります。希望する場合はag
、でラップしてみてくださいstdbuf
。stackoverflow.com/questions/28982518/…–
do exit ;
。それはうまくいくように見えましたが、尻尾は終わらず、スクリプトは次の行に移動しませんでした。do
セクションに尻尾を止める方法はありますか?
この質問はすでに答えられているように見えますが、より良い解決策があると思います。
むしろ、tail | whatever
あなたが本当に欲しいものはあると思いますswatch
。Swatchは、ユーザーが求めていることを実行し、ログファイルを監視し、ログ行に基づいてアクションを実行するために明示的に設計されたプログラムです。使用tail|foo
するには、これを行うために端末をアクティブに実行する必要があります。一方、スウォッチはデーモンとして実行され、常にログを監視しています。SwatchはすべてのLinuxディストリビューションで利用可能です。
ぜひお試しください。ドライバーの裏側で釘を打ち込むことはできますが、そうすべきではありません。
私が見つけたスウォッチに関する最高の30秒のチュートリアルはこちらです:http : //www.campin.net/newlogcheck.html
それがどれほどシンプルで読みやすいかを見てみましょう:
mylog() {
echo >>/path/to/myscriptLog "$@"
}
while read line;do
case "$line" in
*"Printer on fire"* )
mylog Halting immediately
shutdown -h now
;;
*DHCPREQUEST* )
[[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\ ]]
mylog Incomming or refresh for ${BASH_REMATCH[1]}
$HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
;;
* )
mylog "untrapped entry: $line"
;;
esac
done < <(tail -f /path/to/logfile)
bashを使用しない場合regex
、これは非常に高速になります!
しかし、高負荷サーバーの場合、およびsed
非常に高速で非常にスケーラブルであるため、私はこれをよく使用します。
while read event target lost ; do
case $event in
NEW )
ip2int $target intTarget
((count[intTarget]++))
...
esac
done < <(tail -f /path/logfile | sed -une '
s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW \1/p;
s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL \1/p;
...
')
それは私がこれを始めた方法ですが、それではるかに洗練されました。考慮すべきいくつかのこと:
私はこれに沿って何かを使用します:
RELEASE=/tmp/${RANDOM}$$
(
trap 'false' 1
trap "rm -f ${RELEASE}" 0
while ! [ -s ${RELEASE} ]; do sleep 3; done
# You can put code here if you want to do something
# once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}
ファイルにデータが含まtail
れるまで開いたままにすることで機能し${RELEASE}
ます。
一度grep
それを成功します。
${RELEASE}
ます${wait_pid}
プロセスを終了するtail
注:起動時に生成されるsed
行数を実際に決定し、その数をtail
削除するために、より洗練されたものにすることができます。しかし、一般的には10です。
tail -F
回転をログに処理するために-つまりはmy.log
いっぱいになり、に移動my.log.1
し、あなたのプロセスが新しい作成my.log