私は通常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
context
5番目と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