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

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

ほぼ全部探索してみたみんな 書くことができる 似たような 質問、役に立たない。

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

stdout行とstderr行を生成する無人スクリプトを実行し、それをキャプチャしたいと思います。端末エミュレータに表示されている正確な順序に従ってください。次に、「STDERR:」や「STDOUT:」などの接頭辞を追加します。

私はパイプとepollベースの方法を試しましたが、成功しませんでした。私はこの分野の専門家ではありませんが、解決策はptyの使い方にあると思います。ソースコードも見ました。GNOME用VTEしかし、これはあまり効果がありませんでした。

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

似たようなことができる人はいますか?それともこれは不可能ですか?私の考えでは、ターミナルエミュレータがこれを行うことができれば、それはできません。おそらく、PTYを別の方法で処理する小さなCプログラムを作成するのではないでしょうか?

理想的には、非同期入力を使用して両方のストリーム(STDOUTとSTDERR)を読み込み、必要に応じて再印刷しますが、入力順序は重要です!

メモ:わかりましたストライディッドただし、Bashスクリプトでは機能しないため、プレフィックスを追加するために簡単に編集することはできません(デフォルトでは複数のシステムコールをラップするため)。

修正する:次の2つを追加しました。

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

修正する:この問題に対する解決策も解決されます。これは別の質問です、@Gillesが指摘したように。しかし、私は次の結論に達しました。不可能あちこちで要求どおりに行ってください。 2つのストリームを使用している場合は、2>&1pty / pipeレベルで正しくマージされますが、ストリームを個別に正しい順序で使用するには、実際には次の方法を使用する必要があります。ストライディッド次のように表示されるシステムコールフックが含まれています。汚いあらゆる点で。

誰もが上記のポイントに反論できる場合は、喜んで質問を更新します。

答え1

コルーチンも使用できます。マークアップを実行する2つのsedインスタンス(もう一方はstderr他方)に指定されたコマンドの2つの出力を提供する単純なラッパーです。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そうしないと、カーネルスケジューラがこれを台無しにする可能性があります。

    質問が正しく理解されている場合は、2つのストリームを1つのプロセスにリダイレクトするようにシェルに指示する必要があります(これは完了することができることを理解しています)。問題は、プロセスが最初に何を行うかを決定し始めたときに始まります。プロセスは2つのデータソースをポーリングする必要があり、ある時点で1つのストリームが処理され、データが完了する前に両方のストリームに到達する状態になります。これが崩れるところです。これはまた、このような出力システムコールをラップすることがstderred望ましい結果を得る唯一の方法である可能性があることを意味します(そしてそのような場合でも、マルチプロセッサシステムでマルチスレッドになると問題が発生する可能性があります)。

共同プロセスについては、Stéphaneの記事を必ずお読みください。Bashでcoprocコマンドを使用する方法は?深い洞察力を得るためです。

答え2

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

このSO Q&Aのソリューションを使って同様のことをするのはどうですか?テキスト行の前にタイムスタンプを追加できるUnixユーティリティはありますか?このSO Q&Aのタイトルは次のとおりです。シェルスクリプトでSTDOUTとSTDERRを2つの異なるプロセスにパイプしますか?

この方法

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

$ 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

はい

ここでは、STDOUTに書き込んaで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

タイムスタンプ部分の出力形式を制御できますが、より多くの部分は制御できません。ただし、出力はあなたが探しているものと似ているので、あなたの声明に合うかもしれません。

答え3

スクリプトが基本的に出力を2つの別々のパイプに分割することを考えると、これらのパイプに接続された単一のプログラムであっても、行が置き換えられていないことを100%確信することは物理的に不可能です。ある時点では、交互に非遮断読み取りを実行する必要があります。次に読み込むパイプを決定する必要があります。つまり、端末エミュレータでさえ、行の順序を正しく維持できないようです。

関連情報