grepの後、パイプコマンドは機能しません。

grepの後、パイプコマンドは機能しません。

ファイルを監視したい。そのため、1行に1つのスクリプトを実行するために、次のコマンドを使用してファイルを追跡しました。

tail -3f logfile.log | grep "action.*add" | sed -u -e "s/^/'/" -e "s/$/'/" | xargs -L 1 -P 5 bash testscript.sh

しかし、スクリプトが実行されていないようです。grep次のパイプには入力がないことがわかりました。

私が試したとき、

tail -3f logfile.log | grep "action.*add"

効率的。ただし、次のフィルタ(例:sedなど)が与えられると、以下のようには機能しません。grepxargs

tail -3f /var/tmp/rabbitmq-tracing/logfile.log | grep "action.*add" | grep add 

これが起こる理由とこの問題を克服する方法を理解するのに役立ちます。


編集1: デフォルトでは、次の作業が機能しなければなりませんでした。なぜ今はうまくいかないのか混乱しています。

tail -f file.txt | grep something | grep something | grep something

編集2: 最初のgrep以降の出力行は、以下のようにjson文字列になります。この行をbashスクリプトへの入力として使用したいと思います(一重引用符で囲みます)。

{"twNotif": {"originator": "api", "chain": "test", "txId": "08640-0050568a5514", "version": "1.0", "msgType": "api", "twData": {"api": {"hostId": "007bdcc5", "user": "test", "cmdTxt": "100599"}}, "action": "add", "store": "test", "msgTime": 1467280648.971042}}

答え1

--line-bufferedスイッチの使用grep

tail -3f logfile.log | grep --line-buffered "action.*add" | sed -u -e "s/^/'/" -e "s/$/'/" | xargs -L 1 -P 5 bash testscript.sh

男のgrepから:

--line-buffered 出力にラインバッファリングを使用します。これによりパフォーマンスが低下する可能性があります。


またはあなたは使用することができますstdbuf もっと読む

stdbufを使用すると、プログラムに関連する3つの標準I / Oストリームのバッファリング操作を変更できます。要約:

次の構文を使用してください。

... | stdbuf -oL grep ... | ...

あなたの例:

tail -3f logfile.log | stdbuf -oL grep "action.*add" | sed -u -e "s/^/'/" -e "s/$/'/" | xargs -L 1 -P 5 bash testscript.sh

答え2

他の人が指摘したように、grepラインバッファリングが問題の明らかな原因です。

ただし、現在実行中の作業にはあまり明確ではありませんが、他の問題もあります。

sedまず、に入力できるように、各出力行の先頭と末尾にアポストロフィを追加するようですxargs。これは必要ありません。オプションをxargs使用して、-d区切り文字で改行を使用するように指示できます。入力ラインがないときに何もしないようにすることです)xargs -d'\n' -r-rxargs

次に、正規表現を使用してjsonデータを解析します。正規表現を使用したXMLまたはHTMLの解析により、複雑でネストされた構造を処理することは信頼できず、非常に困難または不可能で壊れやすく、突然の衝突が発生しやすいです。 XMLまたはHTMLを解析しません。正規表現の使用。それ動作しませんJSONにも同様に適用されます。

代わりに、またはjsonデータからフィールドを抽出するなどjqのものを使用する必要があります。jsonpipeたとえば、

jq -c 'select(.twNotif.action == "add")' file.txt | 
  xargs -d'\n' -r -L 1 -P 5 ./testscript.sh

ただパイプしたいなら作業フィールド値xargs(二重引用符なし)次のことができます。

jq 'select(.twNotif.action == "add") | .twNotif.action' file.txt | 
  sed -e 's/"//g' | 
  xargs -d'\n' -r -L 1 -P 5 ./testscript.sh

次の作業に使用するのはjsonpipe簡単です。awk

jsonpipe < file.txt | 
  awk '$1 == "/twNotif/action" {gsub(/"/,""); print $2}' |
  xargs -d'\n' -r -L 1 -P 5 ./testscript.sh

(ここのリダイレクトはとは異なり、jqstdinjsonpipeでのみ機能します。)

ところで、jsonデータを、、jsonpipeなどの行ベースのツールで使用するのに適した行ベースの形式に変換します。例えば:sedgrepawk

$ jsonpipe < file.txt
/   {}
/twNotif    {}
/twNotif/originator "api"
/twNotif/chain  "test"
/twNotif/txId   "08640-0050568a5514"
/twNotif/version    "1.0"
/twNotif/msgType    "api"
/twNotif/twData {}
/twNotif/twData/api {}
/twNotif/twData/api/hostId  "007bdcc5"
/twNotif/twData/api/user    "test"
/twNotif/twData/api/cmdTxt  "100599"
/twNotif/action "add"
/twNotif/store  "test"
/twNotif/msgTime    1467280648.971042

jq特にjson形式の出力が不要な場合は、より使いやすくなります。

たとえば、次のように簡単に使用できますjq

$ jsonpipe <file.txt | awk '{gsub(/\//,"."); print $1}'
.
.twNotif
.twNotif.originator
.twNotif.chain
.twNotif.txId
.twNotif.version
.twNotif.msgType
.twNotif.twData
.twNotif.twData.api
.twNotif.twData.api.hostId
.twNotif.twData.api.user
.twNotif.twData.api.cmdTxt
.twNotif.action
.twNotif.store
.twNotif.msgTime

答え3

あなたが見ている問題はおそらく「パイプバッファリング」です。パイプ内のさまざまなコンポーネントの出力は(あなたの場合grep)もはやラインバッファリングではなくブロックバッファリングであり、バッファはおそらく次のコンポーネントではなく4Kです。すぐにデータを参照してください。それ〜するデータが十分に入ってきたら見てください...

解決策は少し痛いです。幸いexpect、パッケージには役立つコマンドが付属していますunbufferman unbuffer使用方法の詳細です。

これは、パイプラインのコンポーネントが端末と通信していると考えさせ、ラインをバッファリングされたままにすることによって機能します。

関連情報