私の関数がパイプの入力として呼び出されたことをどうやって確認しますか?

私の関数がパイプの入力として呼び出されたことをどうやって確認しますか?

私は通常if [ -t 0 ]stdinをテストし、if [ -t 1 ]stdinとstdoutがTTYであることを確認し、そうでない場合はパイプであるとします。私は最近、これが間違った仮定であることに気づきました。

function context()
{
    if [ -t 0 ]
    then
        if [ -t 1 ]
        then
            echo "no pipes"
        else
            echo "pipe out only"
        fi
    else
        if [ -t 1 ]
        then
            echo "pipe in only"
        else
            echo "pipe in and out"
        fi
    fi
}

echo "No Loop:"        # these cases work as desired
context
echo 'f' | context
echo 'f' | context | cat
context | cat

echo
echo "Loop:"
echo $'foo
bar' | while read x
do
    context             # I want this to say "no pipes"
    echo 'f' | context
done

上記のbashの出力は次のとおりです。

No Loop:
no pipes
pipe in only
pipe in and out
pipe out only

Loop:
pipe in only
pipe in only
pipe in only
pipe in only

context5番目と7番目の呼び出しが「パイプなし」を印刷するようにtoの定義をどのように変更しますか?

つまり、stdinが端末でないかどうかをテストする代わりに(問題を引き起こす可能性があります)、この特定の関数のstdinがパイプであるかどうかをテストしたいのです。-p名前付きパイプであることを確認するのを見ましたが、匿名パイプに使用したいと思います。

編集する

実際のユースケースには、代わりtheDbに呼び出す関数が含まれますcontext。理想的には、次の2つのケースは状況に関係なく同じです。

cat file_with_some_sql | theDb
theDb "sql goes here"

答え1

理由「パイプ入力のみ」4回発生するのは、実際にcontext関数が正しく機能するためです。echo $'foo bar' | while read x など。パイプです -すべてループはwhileパイプから入力を受け取ります。希望の出力を得るには、whileループをforループに変更し、何もパイプに接続しないでくださいfor

for x in foo bar; do  context ;  echo 'f' | context; done

出力:

no pipes
pipe in only
no pipes
pipe in only

OPコードがどこで間違っているかを確認するには、forループにいくつかの内容を入力し、何が起こるかを確認してください。

echo | for x in foo bar; do  context ;  echo 'f' | context; done

出力:

pipe in only
pipe in only
pipe in only
pipe in only

初心者はwhile read xすべての標準入力が消費されると(間違って)考えることができますが、実際にはそうではありません。read内部ループを含む次の例を考えてみましょうwhile

printf '%s\n' foo bar baz buzz | 
while read x; do echo $x; read y; echo ${y^^} ;done

出力:

foo
BAR
baz
BUZZ

関連情報