web-dev-qa-db-ja.com

シェルスクリプトでファイルを待機するにはどうすればよいですか?

/tmpという名前のsleep.txtディレクトリにファイルが表示されるのを待機するシェルスクリプトを記述しようとしています。見つかった場合、プログラムは停止します。それ以外の場合は、プログラムをファイルが見つかるまでスリープ(一時停止)状態。ここで、テストコマンドを使用することを想定しています。だから、のようなもの

(if [ -f "/tmp/sleep.txt" ]; 
then stop 
   else sleep.)

私はシェルスクリプトを書くのが初めてで、どんな助けも大歓迎です!

16
Mo Gainz

Linuxでは、 inotify カーネルサブシステムを使用して、ディレクトリ内のファイルの出現を効率的に待機できます。

_while read i; do if [ "$i" = sleep.txt ]; then break; fi; done \
   < <(inotifywait  -e create,open --format '%f' --quiet /tmp --monitor)
# script execution continues ...
_

<()出力リダイレクト構文のBashを想定)

のような固定時間間隔のポーリングと比較したこのアプローチの利点

_while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# script execution continues ...
_

カーネルがより多くスリープするということです。 _create,open_のようなinotifyイベント仕様では、_/tmp_の下のファイルが作成または開かれたときに、スクリプトの実行がスケジュールされます。固定時間間隔のポーリングでは、時間の増分ごとにCPUサイクルを浪費します。

ファイルが既に存在する場合に_touch /tmp/sleep.txt_も登録するためにopenイベントを含めました。

25
maxschlepzig

テストをwhileループに入れるだけです。

while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# next command
10
jimmij

これまでに提供されたinotifywaitベースのアプローチのいくつかには、いくつかの問題があります。

  • 最初に一時的な名前として作成され、次にsleep.txtに名前が変更されたsleep.txtファイルを見つけることができません。 createに加えてmoved_toイベントにも一致する必要があります
  • ファイル名には改行文字を含めることができます。改行で区切られたファイルの名前を印刷しても、sleep.txtが作成されているかどうかを判断するのに十分ではありません。たとえば、foo\nsleep.txt\nbarファイルが作成された場合はどうなりますか?
  • ファイルが作成された場合はどうなりますかbeforeinotifywaitが開始され、時計がインストールされましたか?次に、inotifywaitは、すでにここにあるファイルを永久に待ちます。ファイルがまだ存在していないことを確認する必要があります時計がインストールされています。
  • 一部のソリューションでは、ファイルが見つかった後、(少なくとも別のファイルが作成されるまで)inotifywaitを実行したままにします。

これらに対処するには、次のようにします。

sh -c 'echo "$$" &&
        LC_ALL=C exec inotifywait -me create,moved_to --format=/%f/ . 2>&1' | {
  IFS= read pid &&
    while IFS= read -r line && [ "$line" != "Watches established." ]; do
      : wait for watches to be established
    done
  [ -e sleep.txt ] || [ -L sleep.txt ] || grep -qxF /sleep.txt/ && kill "$pid"
}

現在のディレクトリsleep.txt.が作成されるのを監視していることに注意してください(この例では、前にcd /tmp || exitを実行します)。現在のディレクトリは変更されないため、そのパイプラインが正常に戻ると、作成されたのは現在のディレクトリのsleep.txtです。

もちろん、上記の./tmpに置き換えることができますが、inotifywaitの実行中に、/tmpの名前が数回変更された可能性があります(/tmpの場合とは異なり、しかし、一般的なケースで考慮すべきこと)またはそれにマウントされた新しいファイルシステムなので、パイプラインが返されたときに、作成されたのは/tmp/sleep.txtではなく/new-name-for-the-original-tmp/sleep.txtである可能性があります。新しい/tmpディレクトリも間隔内に作成されている可能性があり、そのディレクトリは監視されないため、そこに作成されたsleep.txtは検出されません。

7

受け入れられた回答は実際に機能します(maxschlepzigに感謝します)が、スクリプトが終了するまでinotifywaitの監視をバックグラウンドに残します。 inotifywaitによって監視されるディレクトリがドット(。)から次のように変更されている場合、exactly要件(つまり、sleep.txtが/ tmp内に表示されるのを待つ)に一致する唯一の回答は、Stephaneの回答のようです。 '/ tmp'。

ただし、一時ディレクトリを使用する場合[〜#〜] only [〜#〜]を使用してsleep.txtフラグを配置し、他の誰もそのディレクトリにファイルを配置しないようにすることができます。ファイルの作成についてこのディレクトリを監視するようinotifywaitに要求するだけで十分です。

最初のステップ:監視するディレクトリを作成します。

directoryToPutSleepFile=$(mktemp -d)

第2ステップ:ディレクトリが実際に存在することを確認します

until [ -d $directoryToPutSleepFile ]; do sleep 0.1; done

3番目のステップ:$directoryToPutSleepFile内にファイルが表示されるまで待ちます

inotifywait -e create --format '%f' --quiet $directoryToPutSleepFile

$directoryToPutSleepFileに入れるファイルには、sleep.txt awake.txtという名前を付けることができます。 anyファイルが$directoryToPutSleepFile内に作成された瞬間、スクリプトはinotifywaitステートメントを過ぎて続行します。

1
nikolaos

一般に、単純なテスト/スリープループ以外の信頼性を高めることは非常に困難です。主な問題は、テストがファイルの作成と競合することです。テストが繰り返されない限り、作成イベントが失われる可能性があります。これは、シェルを使用して開始するほとんどの場合に断然最善の策です。

#!/bin/bash
while [[ ! -e /tmp/file ]] ; do
    sleep 1
done

パフォーマンスの問題が原因でこれで不十分であることがわかった場合は、シェルを使用することはおそらく最初から優れたアイデアではありません。

0
n-alexander

Maxschlepzigの承認された回答(および https://superuser.com/questions/270529/monitoring-a-file-until-a-string-is-found の承認された回答からのアイデアに基づく)私は、タイムアウトでも機能する次の改善された(私の見解では)回答を提案します。

# Enable pipefail, so if the left side of the pipe fails it does not get silently ignored
set -o pipefail
( timeout 120 inotifywait -e create,open --format '%f' --quiet /tmp --monitor & ) | while read i; do if [ "$i" == 'sleep.txt' ]; then break; fi; done
EXIT_STATUS=$?
if [ "${EXIT_STATUS}" == '124' ]; then
  echo "Timeout happened"
fi

指定されたタイムアウト内にファイルが作成または開かれない場合、終了ステータスは124です(タイムアウトのドキュメント(マニュアルページ)のとおり)。作成/オープンされた場合、終了ステータスは0(成功)です。

はい、inotifywaitはこの方法でサブシェルで実行され、そのサブシェルは、タイムアウトが発生したとき、またはメインスクリプトが終了したとき(どちらか早い方)にのみ実行を終了します。

0
Attila123