web-dev-qa-db-ja.com

エレガントな方法で永遠に何もしない方法は?

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)。どうすればこれを達成できますか? (ここでの優雅さの私の主な基準:一時ファイルなし、ビジー待機または定期的なウェイクアップなし、エキゾチックなユーティリティなし、可能な限り短い)。

87
a3nm

それらをサポートするシェル(ksh、zsh、bash4)では、programco-process として開始できます。

  • kshprogram > output |&
  • zshbashcoproc program > output

これは、バックグラウンドでprogramを開始し、その入力はpipeからリダイレクトされます。パイプのもう一方の端はシェルに開いています。

そのアプローチの3つの利点

  • 追加のプロセスなし
  • programが終了したときにスクリプトを終了できます(それを待つにはwaitを使用します)
  • programは終了します(シェルが終了した場合は、標準入力でeofを取得します)。
17

私はあなたがこれよりエレガントになるとは思わない

tail -f /dev/null

あなたがすでに提案したこと(これが内部でinotifyを使用すると仮定すると、ポーリングやウェイクアップはないはずなので、奇妙に見えること以外はそれで十分です).

無期限に実行され、stdoutを開いたままにしますが、実際にはstdoutに何も書き込まず、stdinが閉じられても終了しないユーティリティが必要です。 yesのようなものが実際にstdoutに書き込みます。 catは、そのstdinが閉じられると終了します(または、そこにリダイレクトするものはすべて終了します)。おもう sleep 1000000000dは機能するかもしれませんが、tailの方が明らかに優れています。私のDebianボックスには、コマンドを少し短くするtailfがあります。

別の方法で、screenでプログラムを実行してみませんか?

81
P.T.

_sleep infinity_は、私が知っている最も明確なソリューションです。

infinityを使用できるのは、sleepが浮動小数点数を受け入れるためです*、これはdecimalhexadecimalの場合があります。 、infinity、またはNaN、_man strtod_に応じて。

*これはPOSIX標準の一部ではないため、_tail -f /dev/null_ほど移植性がありません。ただし、GNU coreutils(Linux)およびBSD(Macで使用))でサポートされています(Macの新しいバージョンではサポートされていないようです。コメントを参照してください)。

50
Zaz
sleep 2147483647 | program > output &

はい、 2^31-1は有限数であり、実行されませんforeverですが、最終的にスリープがタイムアウトしたときに$ 1000を差し上げます。 (ヒント:そのうちの1人は死んでしまいます。)

  • 一時ファイルはありません。小切手。
  • ビジー待機または定期的なウェイクアップはありません。小切手
  • エキゾチックなユーティリティはありません。小切手。
  • できるだけ短くしてください。さて、それは短くなる可能性があります。
19
Rob

あなたはそれだけでそれを行うバイナリを作成することができます:

$ echo 'int main(){ pause(); }' > pause.c; make pause
10
PSkocik

ここに別の提案があります標準のUnixユーティリティを使用して、「無期限に何もしない」

sh -c 'kill -STOP $$' | program > output

これにより、SIGSTOPがすぐに送信されるシェルが起動され、プロセスが中断されます。これは、プログラムへの「入力」として使用されます。 SIGSTOPの補数はSIGCONTです。つまり、シェルのPIDが12345であることがわかっている場合は、kill -CONT 12345続行します。

5
roaima

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

2

StéphaneChazelasのreadsolution は、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 $!
0
user6915