プログラムに渡す前に、bashスクリプトでパイプを使用してパイプにデータが含まれているかどうかを確認したいと思います。
「test -t 0
」について検索しましたが、ここでは機能しません。常にfalseを返します。では、パイプにデータがあることを確認するにはどうすればよいですか?
例:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
出力:fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
出力:fill
前述のパイプラインが出力を生成したかどうかをテストする標準/正規の方法とは異なり 入力を保持してプログラムに渡す必要があります。これは一般化します 出力をあるプロセスから別のプロセスにパイプする方法ですが、最初のプロセスに出力がある場合にのみ実行しますか? これは電子メールの送信に焦点を当てています。
一般的に利用可能なシェルユーティリティを使用してパイプの内容を確認する方法はありません。また、パイプから文字を読み取って元に戻す方法もありません。パイプにデータがあることを確認する唯一の方法は、バイトを読み取ることであり、そのバイトを宛先に取得する必要があります。
つまり、1バイトを読み取ります。ファイルの終わりを検出した場合は、入力が空のときに実行したいことを行います。バイトを読み取る場合は、入力が空でないときに何をしたいかをフォークし、そのバイトをそのバイトにパイプし、残りのデータをパイプします。
first_byte=$(dd bs=1 count=1 2>/dev/null | od -t o1 -A n | tr -dc 0-9)
if [ -z "$first_byte" ]; then
# stuff to do if the input is empty
else
{
printf "\\$first_byte"
cat
} | {
# stuff to do if the input is not empty
}
fi
Joey Hessのmoreutils の ifne
ユーティリティは、入力が空でない場合にコマンドを実行します。通常、デフォルトではインストールされませんが、ほとんどのUNIXバリアントで利用できるか、簡単に構築できるはずです。入力が空の場合、ifne
は何もせず、ステータス0を返します。ステータス0は、正常に実行されているコマンドと区別できません。入力が空の場合に何かを実行したい場合は、コマンドが0を返さないようにする必要があります。これは、成功した場合に識別可能なエラーステータスを返させることで実行できます。
ifne sh -c 'do_stuff_with_input && exit 255'
case $? in
0) echo empty;;
255) echo success;;
*) echo failure;;
esac
test -t 0
はこれとは関係ありません。標準入力が端末かどうかをテストします。入力が利用可能かどうかについては、何の意味もありません。
単純な解決策は、ifne
コマンドを使用することです(入力が空でない場合)。一部のディストリビューションでは、デフォルトではインストールされません。ほとんどのディストリビューションではmoreutils
パッケージの一部です。
ifne
は、標準入力が空でない場合にのみ、指定されたコマンドを実行します
標準入力が空でない場合、ifne
を介して指定されたコマンドに渡されます。
古い質問ですが、誰かが私と同じように遭遇した場合:私の解決策は、タイムアウトで読むことです。
while read -t 5 line; do
echo "$line"
done
stdin
が空の場合、5秒後に戻ります。それ以外の場合は、すべての入力を読み取り、必要に応じて処理できます。
stdin(0)のファイル記述子が開いているか閉じているかを確認します。
[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"
短くて不可解なワンライナーが好きなら:
$ echo "string" | grep . && echo "fill" || echo "empty"
string
fill
$ echo "string" | tail -n+2 | grep . && echo "fill" || echo "empty"
empty
元の質問の例を使用しました。パイプされたデータが必要ない場合は、grepで-q
オプションを使用します
Unixで読み取り可能なデータがあるかどうかを確認する簡単な方法の1つは、FIONREAD
ioctlを使用することです。
私はそれだけで実行する標準のユーティリティは考えられないので、ここにそれを実行する簡単なプログラムを示します(moreutils IMHOのifne
よりも優れています;-))。
fionread [ prog args ... ]
Stdinで利用可能なデータがない場合、ステータス1で終了します。データがある場合、prog
を実行します。 prog
が指定されていない場合、ステータス0で終了します。
これは、パイプだけでなく、ほとんどの種類のファイル記述子で機能します。
fionread.c
#include <unistd.h>
#include <poll.h>
#include <sys/ioctl.h>
#ifdef __Sun
#include <sys/filio.h>
#endif
#include <err.h>
int main(int ac, char **av){
int r; struct pollfd pd = { 0, POLLIN };
if(poll(&pd, 1, -1) < 0) err(1, "poll");
if(ioctl(0, FIONREAD, &r)) err(1, "ioctl(FIONREAD)");
if(!r) return 1;
if(++av, --ac < 1) return 0;
execvp(*av, av);
err(1, "execvp %s", *av);
}
(明示的なサブシェルで)test -s /dev/stdin
も使用できます。
# test if a pipe is empty or not
echo "string" |
(test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')
echo "string" | tail -n+2 |
(test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')
: | (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')
バッシュで:
read -t 0
入力にデータがあるかどうかを検出します(何も読み取らないで)。次に、入力を読み取ることができます(読み取りの実行時に入力が使用可能な場合)。
if read -t 0
then read -r input
echo "got input: $input"
else echo "No data to read"
fi
注:これはタイミングに依存することを理解してください。これは、read -t
の実行時にのみ、入力に既にデータがあるかどうかを検出します。
たとえば、
{ sleep 0.1; echo "abc"; } | read -t 0; echo "$?"
出力は1
です(読み取りエラー、つまり空の入力)。エコーは一部のデータを書き込みますが、最初のバイトの開始と書き込みはそれほど高速ではないため、プログラムがまだ何も書き込んでいないため、read -t 0
は入力が空であることを報告します。
最初の行全体を読んでよければ、これはbashでの妥当なifne実装のようです
ifne () {
read line || return 1
(echo "$line"; cat) | eval "$@"
}
echo hi | ifne xargs echo hi =
cat /dev/null | ifne xargs echo should not echo
これは私にとってread -rt 0
データのない元の質問の例:
echo "string" | tail -n+2 | if read -rt 0 ; then echo has data ; else echo no data ; fi
もう1つのハックは、次のようにtimeout
を使用して/dev/stdin
から読み取ることです。
timeout 1s cat /dev/stdin > /tmp/input
if [ $? -eq 124 ] && ! [ -s /tmp/input ]
then
echo "No input provided."
else
echo "Input provided"
# Optionally, you can move or read from /tmp/input...whatever your program needs to do.
fi