stdoutとstderrを2つの別々のストリームに表示する

stdoutとstderrを2つの別々のストリームに表示する

私は、stdout と stderr を視覚的に分離して、それらが重ならないようにし、簡単に識別できるようにする方法を探しています。理想的には、stdout と stderr は、画面上で別々の領域 (たとえば、異なる列) に表示されます。たとえば、出力は次のようになります。

~$ some command
some useful output info
ERROR: an error
more output
ERROR: has occurred
another message
~$ 

代わりに次のようになります:

~$ some command          |
some useful output info  |
more output              |  ERROR: an error
another message          |  ERROR: has occurred
~$                       |

答え1

screenGNUの垂直分割機能を使うこともできます:

#! /bin/bash -
tmpdir=$(mktemp -d) || exit
trap 'rm -rf "$tmpdir"' EXIT INT TERM HUP

FIFO=$tmpdir/FIFO
mkfifo "$FIFO" || exit

conf=$tmpdir/conf

cat > "$conf" << 'EOF' || exit
split -v
focus
screen -t stderr sh -c 'tty > "$FIFO"; read done < "$FIFO"'
focus
screen -t stdout sh -c 'read tty < "$FIFO"; eval "$CMD" 2> "$tty"; echo "[Command exited with status $?, press enter to exit]"; read prompt; echo done > "$FIFO"'
EOF

CMD="$*"
export FIFO CMD

screen -mc "$conf"

たとえば次のように使用します:

that-script 'ls / /not-here'

アイデアは、垂直分割レイアウトで 2 つの screen ウィンドウを起動する一時的な conf ファイルを使用して screen を実行することです。最初のウィンドウでは、2 番目のウィンドウに接続された stderr を使用してコマンドを実行します。

名前付きパイプを使用して、2 番目のウィンドウがその tty デバイスを最初のウィンドウに通信し、また、コマンドが完了したときに最初のウィンドウが 2 番目のウィンドウに通知します。

パイプベースのアプローチと比較したもう 1 つの利点は、コマンドの stdout と stderr が tty デバイスに接続されているため、バッファリングに影響がないことです。両方のペインは、(screenのコピー モードを使用して) 独立して上下にスクロールすることもできます。

このスクリプトを使用して対話的にシェルを実行するとbash、プロンプトが 2 番目のウィンドウに表示されるのがわかります。一方、シェルはプロンプトを stderr に出力するため、最初のウィンドウで入力した内容を読み取ります。

の場合bashエコー入力した内容は2番目のウィンドウにも表示されますエコーはシェル( の場合はreadline bash)によってstderrにも出力されます。 などの他のシェルではksh93、最初のウィンドウに表示されます(エコーまたはを使用してシェルをemacsまたはviモードにしない限り、シェルではなく端末デバイス ドライバーによって出力されます。set -o emacsset -o vi

答え2

annotate-outputこれはDebianのスクリプトに基づいた醜い解決策です注釈出力(1)これがあなたが探しているものかどうかはわかりませんが、まずは次のようなものから始めてください:

#!/bin/bash 

readonly col=150 # column to start error output 

add_out ()
{
    while IFS= read -r line; do
        echo "$1: $line"
    done
    if [ ! -z "$line" ]; then
        echo -n "$1: $line"
    fi
}

add_err ()
{
    while IFS= read -r line; do
        printf "%*s  %s %s: %s\n" $col "|" "$1" "$line"
    done
    if [ ! -z "$line" ]; then
        printf "%*s %s: %s" $col "$1" "$line"
    fi
}

cleanup() { __st=$?; rm -rf "$tmp"; exit $__st; }
trap cleanup 0
trap 'exit $?' 1 2 13 15

tmp=$(mktemp -d --tmpdir annotate.XXXXXX) || exit 1
OUT=$tmp/out
ERR=$tmp/err

mkfifo $OUT $ERR || exit 1

add_out OUTPUT < $OUT &
add_err ERROR < $ERR &

echo "I: Started $@"
"$@" > $OUT 2> $ERR ; EXIT=$?
rm -f $OUT $ERR
wait

echo "I: Finished with exitcode $EXIT"

exit $EXIT

./this_script another_scriptまたは を使用してテストできますcommand

答え3

質問の以下の部分を分析してみたいと思います。

代わりに次のようになります:

~$ 何らかのコマンド
 役に立つ出力情報 |
 さらなる出力 | エラー: エラー
 別のメッセージ | エラー: が発生しました
 ~$

何をしたいのかを分解すると、次のようになります。1

)stdoutストリームは各行を で終わらせるのではなくCR LF、代わりに '|' 文字で終わらせます。もちろん、これでは 2 つのストリームが揃いません。また、 に追加される将来の行の長さを予測する必要があるためstdout、揃えることは不可能です。もちろん、これは不可能です。2

) 揃えを忘れると仮定すると、各行の先頭に "ERROR:" を追加するパイプラインで処理された後、 を単に出力しますstderr。簡単なスクリプトを作成し、 がstderrこのスクリプトから常に出力されるようにすれば、これはかなり簡単だと思います。

ただし、次のような出力が作成されます。

~$ 何らかのコマンド
 役に立つ出力情報|
 出力の詳細| エラー: エラー
 別のメッセージ| エラー: が発生しました

これはあまり役に立ちませんね。また、あなたが求めているのもそれではないと思います。

最初の質問の問題点は、両方のストリームが非同期に書き込まれる可能性があるという事実に関連して、ストリームに追加される各行のシリアルな性質を考慮していないことだと思います。

最も近い解決策は を使用することだと思いますncurses
参照してください。
[http://www.tldp.org/HOWTO/html_single/NCURSES-Programming-HOWTO/]
[[http://invisible-island.net/ncurses/ncurses-intro.html#updating] を参照してください。

目的を達成するには、両方のストリームをバッファリングし、それらを組み合わせて、両方のバッファから要素を取得する3番目のバッファを作成する必要があります。次に、3番目のバッファが変更されるたびにターミナル画面を消去して再描画することで、3番目のバッファをターミナル画面にダンプします。しかし、これがncurses動作方法なので、なぜ車輪の再発明をしてそこから始めないのでしょうか。
いずれにせよ、端末画面の描画方法を完全に引き継ぐ! そして、画面の再印刷バージョンのテキストを、好きなように再配置します。端末の文字を使ったビデオゲームのように。
私の回答が、あなたが求めているものの制限を明確にするのに役立つことを願っています...繰り返して申し訳ありませんが、あなたが示した最大の問題は、およびストリーム
の「プロセッサ」が、適切に配置するために、将来追加される行の長さを事前に知る方法です。stdoutstderr

関連情報