順序付けされたSTDOUT / STDERRをキャプチャし、タイムスタンプ/プレフィックスを追加する方法は?


25

ほぼすべての 利用可能な 同様の 質問を調査しましたが、役に立ちません。

問題について詳しく説明します。

私はいくつかの無人スクリプトを実行し、これらは標準出力と標準エラー行を生成できます。端末エミュレータで表示される正確な順序でそれらをキャプチャし「STDERR:」や「STDOUT:」などのプレフィックスを追加します。

私はパイプを使用してみましたが、それらにエポールベースのアプローチさえ使用しませんでした。解決策はptyの使用法にあると思いますが、私はマスターではありません。また、GnomeのVTEのソースコードを覗きましたが、それほど生産的ではありませんでした。

理想的には、これを実現するためにBashの代わりにGoを使用しますが、できませんでした。パイプは、バッファリングのために正しい行順序を維持することを自動的に禁止しているようです。

誰かが似たようなことをすることができましたか?それとも、単に不可能ですか?ターミナルエミュレーターがそれを行うことができる場合、それはできないと思います-PTY(s)を異なる方法で処理する小さなCプログラムを作成することによるのでしょうか?

理想的には、非同期入力を使用してこれら2つのストリーム(STDOUTおよびSTDERR)を読み取り、2番目に必要に応じて再印刷しますが、入力の順序は重要です!

注:stderredは認識していますが、Bashスクリプトでは機能せず、プレフィックスを追加するために簡単に編集できません(基本的に多くのsyscallをラップするため)。

更新: 2つの要点の下に追加

(一貫した結果を証明するために、提供したサンプルスクリプトに1秒未満のランダムな遅延を追加できます)

更新:この質問に対する解決策は、@ Gillesが指摘したように、この他の質問も解決します。しかし、私は、あちこちで尋ねたことをすることは不可能であるという結論に達しました。使用する場合2>&1、両方のストリームを正しくPTY /管のレベルでマージされているが、別々にストリームと1つが実際のアプローチを使用する必要があり、正しい順序で使用するstderredを involesシステムコールをフックとして見ることができるダーティ多くの方法です。

誰かが上記を反証できるなら、私はこの質問を更新したいと思っています。


1
これはあなたが望むものではありませんか?stackoverflow.com/questions/21564/…–
slm

OPは異なるストリームに異なる文字列を追加する必要があるため、@ slmはおそらくそうではありません。
ペテルフ14

順序が非常に重要である理由を共有できますか?たぶん...あなたの問題を回避する他の方法があるかもしれない
peterph

@peterphそれは前提条件です、一貫した出力を得ることができない場合、それを読んで混乱するよりむしろ/ dev / nullに送信したいです:)2>&1は、例えば順序を保持しますが、種類を許可しません私はこの質問に依頼することをカスタマイズする
Deim0s

回答:


12

コプロセスを使用できます。与えられたコマンドの両方の出力を2つのsedインスタンス(一方stderrは他方のstdout)に送り、タグ付けを行う単純なラッパー。

#!/bin/bash
exec 3>&1
coproc SEDo ( sed "s/^/STDOUT: /" >&3 )
exec 4>&2-
coproc SEDe ( sed "s/^/STDERR: /" >&4 )
eval $@ 2>&${SEDe[1]} 1>&${SEDo[1]}
eval exec "${SEDo[1]}>&-"
eval exec "${SEDe[1]}>&-"

いくつかのことに注意してください。

  1. それは多くの人々(私を含む)にとって魔法の呪文です-理由があります(以下のリンクされた答えを参照)。

  2. ときどきいくつかの行をスワップしないという保証はありません-それはすべて、コプロセスのスケジューリングに依存します。実際、ある時点でそうなることがほぼ保証されています。厳密に同じ順序を保ち、あなたは両方からのデータを処理する必要がある場合には、言ったstderrstdinそれの混乱を作る(なりと)同じプロセスで、それ以外のカーネルスケジューラできます。

    問題を正しく理解していれば、両方のストリームを1つのプロセスにリダイレクトするようシェルに指示する必要があることを意味します(これは知る限りでは可能です)。問題は、そのプロセスが最初に何をすべきかを決定し始めると始まります-両方のデータソースをポーリングし、ある時点で1つのストリームを処理し、終了する前にデータが両方のストリームに到着する状態になる必要があります。そして、それはまさにそれが故障する場所です。また、出力syscallsをラッピングすることは、stderredおそらく目的の結果を達成する唯一の方法であることを意味します(そして、マルチプロセッサシステムで何かがマルチスレッド化されると問題が発生する可能性があります)。

プロセスに関しては、Bashでコマンドcoprocをどのように使用しますか?の Stéphaneの優れた答えを必ず読んでください。深い洞察のために。


あなたの答えを@peterphに感謝しますが、私は特に順序を維持する方法を探しています。注:私は(私が手にあなたのインタプリタがあるため、使用するプロセス置換のbashをされるべきだと思う./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpectedコピーによって/あなたのスクリプトを貼り付け)
Deim0s

可能性が高いので、私はそれを実行しましbash/bin/sh(なぜそこにあったのかわかりません)。
ペテルフ14

ストリームの混乱が発生する可能性のある場所に関する質問を少し更新しました。
ペテルフ14

1
eval $@かなりバグです。"$@"引数を正確なコマンドラインとして実行する場合に使用します- eval解釈のレイヤーを追加すると、予測が困難な束になります(ファイル名やその他の制御しないコンテンツを渡す場合、悪意がある可能性があります)引数)動作、およびさらに引用符を付けない(スペースを含む名前を複数の単語に分割し、以前に引用符で囲まれていたとしてもグロブを展開するなど)。
チャールズダフィー

1
また、現代の十分なコプロセスbashでは、変数で指定されたファイル記述子を閉じる必要ありませんevalexec {SEDo[1]}>&-現状のままで動作します(はい、$以前{は意図的でなかったため)。
チャールズダフィー

5

方法#1。ファイル記述子とawkの使用

このSO Q&Aのソリューションを使用したこのようなものについてはどうですか。「テキストの行にタイムスタンプを追加するUnixユーティリティはありますか?」そして、このSO Q&Aタイトル:STDOUTとSTDERRをシェルスクリプトの2つの異なるプロセスにパイプしますか?

アプローチ

ステップ1、呼び出されたときにタイムスタンプメッセージを実行する2つの関数をBashで作成します。

$ msgOut () {  awk '{ print strftime("STDOUT: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }
$ msgErr () {  awk '{ print strftime("STDERR: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }

ステップ2のように上記の関数を使用して、目的のメッセージングを取得します。

$ { { { ...command/script... } 2>&3; } 2>&3 | msgErr; } 3>&1 1>&2 | msgOut

ここでは、aSTDOUTに書き込み、10秒間スリープした後、STDERRに出力を書き込む例を作成しました。このコマンドシーケンスを上記のコンストラクトに配置すると、指定したとおりにメッセージが表示されます。

$ { { echo a; sleep 10; echo >&2 b; } 2>&3 | \
    msgErr; } 3>&1 1>&2 | msgOut
STDERR: 2014-09-26 09:22:12 a
STDOUT: 2014-09-26 09:22:22 b

方法#2。注釈出力の使用

あなたが望むことannotate-outputをするdevscriptsパッケージの一部であると呼ばれるツールがあります。唯一の制限は、スクリプトを実行する必要があることです。

上記のコマンドシーケンスの例を次のmycmds.bashようなスクリプトに入れた場合:

$ cat mycmds.bash 
#!/bin/bash

echo a
sleep 10
echo >&2 b

その後、次のように実行できます。

$ annotate-output ./mycmds.bash 
09:48:00 I: Started ./mycmds.bash
09:48:00 O: a
09:48:10 E: b
09:48:10 I: Finished with exitcode 0

出力の形式は、タイムスタンプ部分で制御できますが、それを超えることはできません。しかし、それはあなたが探しているものに似た出力なので、法案に合うかもしれません。


1
残念ながら、これはいくつかの行を交換する可能性のある問題も解決しません。
ペテルフ

正確に。私のこの質問に対する答えは「不可能」だと思います。stderredあなたとの出来事は、行の境界を簡単に決定することはできません(そうしようとするとハックします)。私は、誰かがこの問題で私を助けることができるかどうかを確認したかったが、どうやら誰もが単一の制約(あきらめたいため、質問のための基礎である)
Deim0s

方法1のステップ2には、適切に機能するために別の{が必要です。
オースティンハンソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.