uniqとbash for loopは、stdinが閉じられるまでstdoutに書き込まれません(1行のWebサイト訪問者通知システムの場合)。

uniqとbash for loopは、stdinが閉じられるまでstdoutに書き込まれません(1行のWebサイト訪問者通知システムの場合)。

私のウェブサイトを訪問するユニークな訪問者がいるたびに、私のコンピュータのスピーカーからビープ音を鳴らそうとします。

少しブレーンストーミングした後は、次の1行で達成できるようです。

for e in `ssh me@mymachine "tail -n 1 -f /var/log/apache2/test.log | awk '{print $1}' | uniq"`; do beep; done

しかし、stdinが開いている限り、uniqは何も出力しません(EOFを待っているようです)。 forループも同様です。チェーンからuniqを削除してもまだ出力は出ませんが、テールはパイプを開いたままにします。

バッファリングのせいではないようです。このコマンドの実行中にテストファイルに100,000行以上を書き込んでも反対側に出力はありません。

ソリューションの美しさ(シンプルさ)を完全に破壊せずに機能させる方法はありますか?

修正する

最初の部分を解決しました。 uniq は tail コマンドを接頭辞で貼り付けてブロックを解除しますstdbuf -oL -eL(参照:https://unix.stackexchange.com/a/25378/109296)。ループでは動作しません。

アップデート2

私は正しく動作しましたが、私の仕様とまったく一致しません、そして2行あります。

while [ 1 -eq 1 ]; do ssh root@speedy "stdbuf -oL -eL tail -n 1 -f /var/log/apache2/www.access.log | stdbuf -oL -eL grep 'GET / '"; sleep 60; done > www.log

awk '{print $1}'この構成では機能しないため、欠落しています(行全体を渡すだけ)。理由はわかりません。でも、uniqただ見にすぎなかったのでどうせあまり役に立たなかったようだったのでなくてもできました。 近いいいですね。つまり、ip1、ip2、ip1パターンを要求すると、ip1はまだ2回通過します。 uniq -u期待どおりに動作しますが、同じ問題がありますsort。 stdinが開いている間は何も出力されません(stdbuf -oL

このコマンドは、ベースURL(/)に対するすべての要求を他のファイルに書き込みます。何らかの理由でパイプまたは接続が中断された場合は、自動的に再試行するようにループして、待機しました。

while inotifywait -e modify www.log; do beep -f 250; done 騒音を!バッファリングなしで1行ずつ処理するためにbash forループを取得できず、while read同じ結果で試しました。だからあきらめて続けましたが、inotifywaitこれは中間ファイルが必要であることを意味します(おそらく名前付きパイプも機能するかもしれませんが、試していません。実際に私に違いはありません)。

(複雑さを追加せずに)純訪問者をフィルタリングするのに役立つ貢献に感謝します。

私たちのチームメンバーがオフィスに戻るとき、これは大きな驚きになります:-)

私はこの通知システムを拡張し、さまざまなオーディオを使用して複数のイベントを監視する予定です。ほこりがたまっている古いサーバーの場合、これは私が今まで見つけた最高の仕事です...

答え1

私はあなたが達成したいことを理解していると思います。

  1. WebサイトをクリックするたびにWebサーバーによって記録されます。
  2. アクセスが「固有」の場合(これをどのように定義しますか?)、エントリが記録され、サウンド通知が送信されます。

秘訣は「固有」を定義する方法です。 URL、IPアドレス、Cookieを介して行われますか? awkを使用するアプローチは間違いなく正しいアプローチですが、シェルエスケープルールが付いています。

ここにあなたの方法を組み合わせたものがあります。まず、これを行うにはWebサーバーにスクリプトが必要です。それ以外の場合は、複雑な引用エスケープ規則に陥ります。第二に、あなたのウェブサーバーが「共通ログ形式」を使用しているとします。正直なところ、この種の作業には悪いですが、使用できます。

while true; do 
  ssh root@speedy remote-log-capturing-script
done > unique-visits.log

MAILFILEに関するmikeservの素晴らしい提案を使用してください。 Speedyのスクリプトは次のようになります。

#!/bin/sh
tail -1f /var/log/apache2/www.access.log | 
awk '$(NF-1) == 200' | 
grep --line-buffered -o '"GET [^"]*"' |
awk '!url[$1]{ print; url[$1]=1 }'

awk は常にラインバッファリングされます。最初のawkは、キャッシュヒットまたは404ではなく、実際の成功したヒットのみを取得することを保証します。 grep -oは、入力の一致部分(この場合はURL)のみを印刷します。 (これはGNU grepです。使用しているとします。そうでない場合はstdbufトリックを使用してください。)次のawkは、小さな式を使用して入力行を条件付きで印刷します。その入力ラインが以前に見たことがない場合にのみ可能です。

Perlを使用してこれを実行して、ブランチ内でより多くの複雑さを達成することもできます。

#!/bin/sh
tail -1f /var/log/apache2/www.access.log | 
perl -lane '$|=1;' \
  -e 'if ($F[$#F-1] eq "200" and ' \
  -e ' /\s"GET\s([^"]*)"\s/ and !$url{$1}) { '\
  -e '  print $1;$url{$1}=undef; }'

両方とも一意のURLのみを印刷します。異なるIPの2つのWebクライアントが同じページにアクセスした場合はどうなりますか? 1つの出力しか取得できません。これを変更するには、Perlを使用するソリューションが簡単です。 URLでキーを変更するだけです。

 $url{$F[0],$1}

perl -a を使用する場合、 $F[0] は awk の $1 と同様に、最初に入力されたスペースで区切られたフィールド、つまり接続ホスト名/IP アドレスを表します。 Perlの$ 1は、/\s"GET\s([^"]*)"\s/URL自体である正規表現の最初の一致サブ式を表します。ミステリーとは、$F[$#F-1]入力ラインの最後の2番目のフィールドを意味します。

答え2

これがJJoaoの簡潔なPerlコマンドのおかげで、私がついに思い出したものです:

#終了時にすべてを終了します。
トラップ "kill 0" SIGINT SIGTERM
#終了時にリモートプロセスが終了することを確認してください。 http://unix.stackexchange.com/questions/103699/kill-process-spawned-by-ssh-when-ssh-diesを参照してください。
shopt -s huponexit
( while [ 1 -eq 1 ]; do ssh -t -t root@speedy "stdbuf -oL -eL tail -n 1 -f /var/log/apache2/www.access.log | stdbuf -oL -eL grep ' GET / ' | stdbuf -oL -eL perl -naE '($a{$F[0]}++ == 0) $F[0]'"; sleep 60; done > www.log ) &
(同時に、inotifywait -eはwww.logを修正し、ビープ音を鳴らします。-f 250;完了)&

関連情報