stdout のリダイレクトは改行のない行を無視します

stdout のリダイレクトは改行のない行を無視します

ターミナルに赤く印刷されるようにしようとしていますstderr。以下のスクリプトは、デバッグ トラップ時に2カスタムにリダイレクトします。8

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "${RED}${line}${COLORRESET}"
    done
)
function undirect(){ exec 2>&9; } # reset to original 9 (==2)
function redirect(){ exec 2>&8; } # set to custom 8
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

それはここ、わかりやすい説明付き。

非常にうまく動作しているようですが、改行で終了しない入力はまったく印刷されません。著者の言葉を引用しますゴスペスまた:

bash> echo -en "hi\n" 1>&2
    hi       <-- this is red
bash> echo -en "hi" 1>&2
bash> echo -en "hi" 1>&2
bash> echo -en "hi\n" 1>&2
    hihihi   <-- this is red

理由がわかりません。改行以外のコンテンツは、何らかのバッファに収まるようです。ファイル記述子に到達しないか8、すぐには印刷されません。どこに行くのでしょうか? はredirect毎回適切に呼び出されます。また、は区切り文字がないことを意味するので、行単位IFS=''でエコーアウトされる理由がよくわかりません。8

バグ修正をいただければ幸いです。この質問に引用した回答をリンクしました。

この解決策全体は、Gilles が指摘したように、完璧ではありません。読み取り、stdin、プログレスバーに問題があり、どちらもできませんsusourceまた、パイプの破損や予期しないターミナルの終了などの重大な問題が頻繁に発生します。私のリンクからここにたどり着いた方は、ぜひ使用を検討してください。https://github.com/sickill/stderred より代わりに、はるかに良くなりました(まだ問題はありません)(ただし、echo bla >&2赤くなく、それぞれの問題は解決済みです

答え1

改行が印刷された時点で、同じ行の一部として部分的な行の出力が得られました。行の部分はread、内にバッファリングされます。それがその機能です:

読むユーティリティは標準入力から1つの論理行を読み込む

たとえば、これは<foobar>ではなく 1 秒後に印刷されます<foo><bar>

(echo -n foo ; sleep 1 ; echo bar) | (read x ; echo "<$x>")

入力を完全な行ではなく小さな部分でキャッチしたい場合は、Perl などを使用して別の方法を実行する必要があります。これは、<foo><bar\n>(最後の の前に改行を付けて)を出力します。>とは異なり、readPerl は最後の改行を特別に処理しないためです。色付けでは問題にならないはずです。)

(echo -n foo ; sleep 1 ; echo bar) | 
    perl -e '$|=1; while(sysread STDIN,$a,9999) { print "<$a>"}'

環境で色の制御コード (REDおよび) がエクスポートされている場合は、次のように Perl スクリプトからそれらを使用できます。COLORRESET

perl -e '$|=1; while(sysread STDIN,$a,9999) {print "$ENV{RED}$a$ENV{COLORRESET}"}'

答え2

Bash では、組み込み-dのオプションを使用してread、行末記号を定義できます。man bashこれは、次のように述べています。

-d delim    The first character of delim is used to terminate the input line, 
            rather than newline.

定義されていない場合は、文字列を 1 行として扱い、出現をread待ちます。ただし、オプションを使用する場合は、区切り文字として設定できます。もちろん、その場合は入力を NUL で終了する必要もあります。\n-dNUL

例:

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s\n" "$line"
    done

出力:

x

y
z

もう一度言いますが、今度はループprintf内のwhileは と一致しません\n

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s" "$line"
    done

出力:

x
yz(...)

を追加しました。(...)これは、2 行目の最後に End-Of-Line がないことを意味します。ただし、テキストは引き続き処理され、印刷されます。

関連情報