stdout
に関する有用な情報を生成するプログラムを持っていますが、stdin
からも読み取ります。標準入力に何も指定せずに、標準出力をファイルにリダイレクトしたい。これまでのところ、とても良い:私ができること:
program > output
ttyでは何もしません。
ただし、問題はバックグラウンドで実行することです。私が行った場合:
program > output &
プログラムは中断されます(「中断(tty入力)」)。
私が行った場合:
program < /dev/null > output &
eOFに達したため、プログラムは直ちに終了します。
私が必要としているのは、program
にパイプして、無期限に何もせず、stdin
を読み取らないものです。次のアプローチが機能します。
while true; do sleep 100; done | program > output &
mkfifo fifo && cat fifo | program > output &
tail -f /dev/null | program > output &
しかし、これはすべて非常に醜いです。そこにhasは、標準のUnixユーティリティを使用して「無期限に何もしない」(言い換えるとman true
)。どうすればこれを達成できますか? (ここでの優雅さの私の主な基準:一時ファイルなし、ビジー待機または定期的なウェイクアップなし、エキゾチックなユーティリティなし、可能な限り短い)。
それらをサポートするシェル(ksh、zsh、bash4)では、program
を co-process として開始できます。
ksh
:program > output |&
zsh
、bash
:coproc program > output
これは、バックグラウンドでprogram
を開始し、その入力はpipe
からリダイレクトされます。パイプのもう一方の端はシェルに開いています。
そのアプローチの3つの利点
program
が終了したときにスクリプトを終了できます(それを待つにはwait
を使用します)program
は終了します(シェルが終了した場合は、標準入力でeof
を取得します)。私はあなたがこれよりエレガントになるとは思わない
tail -f /dev/null
あなたがすでに提案したこと(これが内部でinotifyを使用すると仮定すると、ポーリングやウェイクアップはないはずなので、奇妙に見えること以外はそれで十分です).
無期限に実行され、stdoutを開いたままにしますが、実際にはstdoutに何も書き込まず、stdinが閉じられても終了しないユーティリティが必要です。 yes
のようなものが実際にstdoutに書き込みます。 cat
は、そのstdinが閉じられると終了します(または、そこにリダイレクトするものはすべて終了します)。おもう sleep 1000000000d
は機能するかもしれませんが、tail
の方が明らかに優れています。私のDebianボックスには、コマンドを少し短くするtailf
があります。
別の方法で、screen
でプログラムを実行してみませんか?
_sleep infinity
_は、私が知っている最も明確なソリューションです。
infinity
を使用できるのは、sleep
が浮動小数点数を受け入れるためです*、これはdecimal、hexadecimalの場合があります。 、infinity、またはNaN、_man strtod
_に応じて。
*これはPOSIX標準の一部ではないため、_tail -f /dev/null
_ほど移植性がありません。ただし、GNU coreutils(Linux)およびBSD(Macで使用))でサポートされています(Macの新しいバージョンではサポートされていないようです。コメントを参照してください)。
sleep 2147483647 | program > output &
はい、 2^31-1
は有限数であり、実行されませんforeverですが、最終的にスリープがタイムアウトしたときに$ 1000を差し上げます。 (ヒント:そのうちの1人は死んでしまいます。)
あなたはそれだけでそれを行うバイナリを作成することができます:
$ echo 'int main(){ pause(); }' > pause.c; make pause
ここに別の提案があります標準のUnixユーティリティを使用して、「無期限に何もしない」。
sh -c 'kill -STOP $$' | program > output
これにより、SIGSTOP
がすぐに送信されるシェルが起動され、プロセスが中断されます。これは、プログラムへの「入力」として使用されます。 SIGSTOP
の補数はSIGCONT
です。つまり、シェルのPIDが12345であることがわかっている場合は、kill -CONT 12345
続行します。
Linuxでは、次のことができます。
read x < /dev/fd/1 | program > output
Linuxでは、/ dev/fd/x(xはパイプの書き込み側のファイル記述子)を開くと、パイプの読み取り側が取得されるため、ここではプログラムのstdinと同じです。そのため、基本的に、read
は決して返されません。そのパイプに書き込む可能性があるのはそれ自体であり、read
は何も出力しないためです。
FreeBSDやSolarisでも動作しますが、別の理由があります。そこで、/ dev/fd/1を開くと、期待どおりに、またLinux以外のほとんどのシステムと同じように、fd 1で開くのと同じリソースが得られるため、パイプの書き込みが終了します。ただし、FreeBSDおよびSolarisでは、パイプは双方向です。したがって、program
がそのstdinに書き込まない限り(アプリケーションは行いません)、read
はパイプのその方向から何も読み取れません。
パイプが双方向でないシステムでは、read
は、書き込み専用のファイル記述子から読み取ろうとすると、おそらくエラーで失敗します。また、すべてのシステムが/dev/fd/x
。
StéphaneChazelasのread
solution は、Mac OS Xでも/dev/fd/1
で読み取りfdが開かれた場合に機能します。
# using bash on Mac OS X
# -bash: /dev/fd/1: Permission denied
read x </dev/fd/1 | cat >/dev/null
echo ${PIPESTATUS[*]} # 1 0
exec 3<&- 3</dev/fd/1
read x 0<&3 | cat >/dev/null
echo ${PIPESTATUS[*]} # 0 0
スクリプト内でtail -f /dev/null
を(たとえばSIGINTを使用して)強制終了できるようにするには、tail
コマンドとwait
をバックグラウンドで実行する必要があります。
#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!