Bash:次のパイプの「no-op」によって「ティー」がブロックされました。

Bash:次のパイプの「no-op」によって「ティー」がブロックされました。
  • tee標準出力がno-op()コマンドにパイプされると、何も印刷されず、:ファイルサイズは0です。
  • tee標準出力をにパイプすると、catすべてが正しく印刷され、ファイルサイズが0よりも大きくなります。

以下はこれを示すコード例です(スクリプトの最初の入力パラメータによる条件付き)。

#!/usr/bin/env bash

log_filepath="./log.txt"
[ -f "$log_filepath" ] && { rm "$log_filepath" || exit 1 ; }

fail_tee="$1"

while IFS= read -r -d $'\n' line ; do
    printf "%s%s\n" "prefix: " "$line"  | \
    tee -a "$log_filepath"              | \
    {
        if [ -n "$fail_tee" ]; then
            # Nothing is printed to stdout/terminal
            # $log_filepath size is ZERO.
            : # do nothing. 
        else
            # Each line in the input is prefixed w/ "prefix: " and sent to stdout
            # $log_filepath size is 46 bytes
            cat
        fi
    }
done <<'EOF'
1
23
456
7890
EOF

その後に説明がありますように。
no-opコマンドに対する私の期待は、出力がファイルに送信されるのを:妨げてはならないということです。tee

答え1

:inはまだシェルで設定されたパイプの読み取り端を保持するプロセスtee ... | :で、もう一方は書き込みteeです。すぐに終了するため、:パイプからデータを読み取ることができません。 (パイプラインが同時に作業を実行するには、シェルは単にno-opを処理するためであっても、パイプラインの各部分に対して新しいプロセスを作成する必要があります。:あなたの例では、プロセスはif最後のステートメントを実行します。:組み込み関数「実行」した後、最終的に終了します)。

一般的な動作は、パイプリーダーが終了したとき(読み取り側のファイル記述子が閉じる)、書き込み機が次の書き込み時にSIGPIPE信号を受信して​​終了するようにすることです。

これは、パイプラインの右側が終了すると左側も終了し、潜在的に長い操作が無駄に続かないことを意味するため、一般的に望むものです。または(悪い場合)、データがどこに行くかがないため、書き込みを許可しないブロックされたパイプに無力に書き込みを試みます。

tee例外はないようだからPOSIX仕様;最も近い部分には、ファイルオペランドへの書き込みエラーが記載されています。

正常にオープンされたファイルオペランドへの書き込みが失敗した場合、正常にオープンされた他のファイルオペランドと標準出力への書き込みは続行する必要がありますが、終了状態はゼロではありません。

SIGPIPEが無視されると、テストされた実装は引き続き実行され、次に呼び出しからEPIPEエラーが返されますwrite()

GNU coreutilsバージョンには、書き込みに失敗したときに実行する操作を制御するオプションがありますtee-p--output-error

指定されていない場合、デフォルトの操作は--output-errorパイプへの書き込み中にエラーが発生した場合は直ちに終了し、非パイプライン出力への書き込み中にエラーを診断することです。

シャットダウン方法はSIGPIPEを介したものであるため、teeシグナルを無視したものから開始しても終了しません。

デフォルトモードは、シャットダウンする他のオプションとは異なり、「パイプラインではなく出力を書き込んでいる間のエラー診断」-pです。warn-nopipe後ろでは、SIGPIPE シグナルも無視し、パイプへの書き込み試行を停止します。

したがって、少なくともGNUバージョンでは、tee -p ... | ...これを使用してパイプリーダーがシャットダウンしたときにシャットダウンするのを防ぎます。あるいは、たとえば、ブラックホールを模倣するようにプログラムを右側に配置することもできますcat > /dev/null(まだ表示されています)。そして取得するすべてを記録しますが、カーネルは最終的に記録されたデータを無視します/dev/null。)

答え2

これは:nopプロセスではありません。これはcat議論の余地がありません。 stdinを読み取らずにstdoutに渡します(これはnopではないため、nopパイプラインステップとして処理できることを見ましたが、これは別のアイデアです)。これはプロセスではありません。

あなたは何でもパイプしようとしています。ケーシングの用途はわかりませんが、パイプが何も接続されていなくても驚くことはありません。 (またはシェルに接続して無視することもできます)。とにかく良いことが起こらなかったらと思います。

@Kusalanandaが言ったように。パイプは、出力からプロセスが読み取られるのを待ちますが(または少なくともパイプを閉じるとき)、どのプロセスもパイプを読み取ったり閉じたりすることはできません。cat >/dev/null[(多型)でパイプできます。または > /dev/null(静的に)リダイレクトできる場合]。

次のいずれか(可能であれば猫がいない方が良い)

| cat >/dev/null
>/dev/null

もう1つの多型ソリューション(重複した猫の削りくず):

if ...
then
  out=/dev/stdout
else
  out=/dev/null
fi

command >"$out"

答え3

:stdinから読み取らないので、PIPE信号を生成します。 PIPE シグナルを受信すると、tee (デフォルト) は終了します。使用すると、:すべてが停止します。

--output-error=warnGNU tee(bashを使用してからすでに使用していると仮定)(te POSIXオプションではありません)を使用している場合は、teeに追加してそれを表示できます。

以下を使用してこれをテストできます。

$ echo "hello" | tee --output-error=warn | :
tee: 'standard output': Broken pipe

:に変更すると、PIPEが終了するのcat >/dev/nullを防ぐことができます。tee

しかし:シェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?

テキストファイル(ここではドキュメント)を1行ずつ読み、パイプに送信するのは最善の選択ではありません。

考慮する:

sed 's/^/preffix: /' <<'EOF' | tee -a "$log_filepath"
1
23
456
7890
EOF

変数値に基づいて配列内でティーオプションを設定することもできます。

また、ドキュメントのここではなく変数に出力を設定します。

#!/bin/bash --

log_filepath="./log.txt"
[ -f "$log_filepath" ] && { rm "$log_filepath" || exit 1 ; }

enable_stdout="${1:+"yes"}"
enable_log="yes" # comment this line to avoid logging.

out=$'1\n23\n456\n7890\n'

unset teeoptions; teeoptions=()
[ -n "$enable_stdout" ] && teeoptions+=(-a "/dev/tty")
[ -n "$enable_log" ]    && teeoptions+=(-a "$log_filepath")

printf '%s' "$out" | 
    sed 's/^/preffix: /' | 
    tee "${teeoptions[@]}" >/dev/null

関連情報