web-dev-qa-db-ja.com

パイプが空かどうかを確認し、空でない場合はデータに対してコマンドを実行する方法は?

プログラムに渡す前に、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

前述のパイプラインが出力を生成したかどうかをテストする標準/正規の方法とは異なり 入力を保持してプログラムに渡す必要があります。これは一般化します 出力をあるプロセスから別のプロセスにパイプする方法ですが、最初のプロセスに出力がある場合にのみ実行しますか? これは電子メールの送信に焦点を当てています。

49
zetah

一般的に利用可能なシェルユーティリティを使用してパイプの内容を確認する方法はありません。また、パイプから文字を読み取って元に戻す方法もありません。パイプにデータがあることを確認する唯一の方法は、バイトを読み取ることであり、そのバイトを宛先に取得する必要があります。

つまり、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のmoreutilsifne ユーティリティは、入力が空でない場合にコマンドを実行します。通常、デフォルトではインストールされませんが、ほとんどの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を介して指定されたコマンドに渡されます。

14
Nick Wirth

古い質問ですが、誰かが私と同じように遭遇した場合:私の解決策は、タイムアウトで読むことです。

while read -t 5 line; do
    echo "$line"
done

stdinが空の場合、5秒後に戻ります。それ以外の場合は、すべての入力を読み取り、必要に応じて処理できます。

9
Ladd

stdin(0)のファイル記述子が開いているか閉じているかを確認します。

[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"
8
mviereck

短くて不可解なワンライナーが好きなら:

$ echo "string" | grep . && echo "fill" || echo "empty"
string
fill
$ echo "string" | tail -n+2 | grep . && echo "fill" || echo "empty"
empty

元の質問の例を使用しました。パイプされたデータが必要ない場合は、grepで-qオプションを使用します

5
yashma

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);
}
4
mosvy

(明示的なサブシェルで)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')
4
trent55

バッシュで:

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は入力が空であることを報告します。

2
Isaac

最初の行全体を読んでよければ、これは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
0
Eric Woodruff

これは私にとってread -rt 0

データのない元の質問の例:

echo "string" | tail -n+2 | if read -rt 0 ; then echo has data ; else echo no data ; fi
0
user333755

もう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
0
Carlos Nunez