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


14

/tmp呼び出されたディレクトリにファイルが表示されるのを待つシェルスクリプトを作成しようとしていますが、sleep.txtそれが見つかるとプログラムは停止します。 。ここで、テストコマンドを使用することを想定しています。だから、のようなもの

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

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


見てください$MAILPATH
mikeserv

回答:


22

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サイクルが無駄になります。

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


ありがとう、これはすごい!ファイルの名前がわかっているのに、なぜwhileループが必要なのですか?なぜそれができなかったのinotifywait -e create,open --format '%f' --quiet /tmp/sleep.txt --monitor > /dev/null ; # continues once /tmp/sleep.txt is created/openedですか?
NHDaly

ああ、jk。ツールをインストールした後、私は今理解しています。inotifywaitそれ自体はwhileループであり、ファイルが開かれる/作成されるたびに行を出力します。したがって、変更されたときにブレークするにはwhileループが必要です。
NHDaly

ただし、テスト-e / sleepバージョンとは異なり、これにより競合状態が発生します。ループに入る直前にファイルが作成されないことを保証する方法はありません。その場合、イベントは失われます。ループの前に(sleep 1; [[-e /tmp/sleep.txt]] && touch /tmp/sleep.txt;)のようなものを追加する必要があります。もちろん、どちらが実際のビジネス・ロジックに応じて、道の問題を作成することができます
のn-アレクサンダー

@ n-alexanderさて、答えはsleep.txt、ある時点で生成されるプロセスを開始する前にスニペットを実行することを前提としています。したがって、競合状態はありません。質問には、想定しているシナリオを示唆するものは含まれていません。
maxschlepzig

逆に@maxschlepzig。順序付けが強制されない限り、想定することはできません。基本。
n-

10

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

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

あなたが書いたように、これはロジックです:ファイルが存在しない間、スクリプトはスリープします。それ以外の場合は完了です。正しい?
Mo Gainz

@MoGainz正しい。後のsleep数字は、各反復で待機する秒数です。必要に応じて変更できます。
-jimmij

7

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

  • 彼らsleep.txtは最初に一時的な名前として作成され、次に名前が変更されたファイルを見つけることができませんsleep.txtmoved_toさらに、イベントに一致する必要がありますcreate
  • ファイル名には改行文字を含めることができますが、改行で区切られたファイル名を出力するだけでsleep.txtは、が作成されたかどうかを判断するのに十分ではありません。foo\nsleep.txt\nbarたとえば、ファイルが作成された場合はどうなりますか?
  • 時計を起動してインストールする前に ファイルが作成された場合はどうなりますinotifywaitか?その後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されないため、そこに作成されたディレクトリは検出されません。


1

受け入れられた答えは実際に動作します(maxschlepzigに感謝します)が、スクリプトが終了するまでバックグラウンドでinotifywait監視を残します。一致していることを唯一の答えを正確にあなたの要件は(すなわち内部に/ tmpを表示するsleep.txtを待っている)がinotifywaitによって監視されるディレクトリは、ドットから変更された場合、ステファンさんのようです(。)「/ tmp」のに。

ただし、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という名前を付けることができます。瞬間任意のファイルが内部で作成された$directoryToPutSleepFileスクリプトが過ぎていきますinotifywait陳述。


1
しかし、直前に別のファイルが作成された場合、sleep.txt2つのinotifywait呼び出しの間にファイルが作成されるため、ファイルの作成を見逃す可能性があります。
ステファンシャゼラス

それに対処する別の方法については私の答えをご覧ください。
ステファンシャゼラス

私のコードを試してください。inotifywaitは一度だけ呼び出されます。sleep.txtが作成されると(または、既に存在する場合は読み書きされます)、inotifywaitは「sleep.txt」を出力し、「done」ステートメントの後に実行が継続されます。
ニコラオス

呼び出されるファイルsleep.txtが作成されるまで、数回呼び出されます。たとえばtouch /tmp/foo /tmp/sleep.txt、を試してから、1つのインスタンスinotifywaitが報告fooして終了します。次のinotifywaitが開始されるまでに、sleep.txtがすでに存在しているため、inotifywaitはその作成を検出しません。
ステファンシャゼラス

複数の呼び出しについては正しいです。/tmpの下に作成されたファイルごとに1回の呼び出しです。回答を更新しました。ご意見をありがとうございます。
ニコラオス

0

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

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

パフォーマンスの問題のためにこれが不十分であることがわかった場合、最初にシェルを使用することはおそらく素晴らしい考えではありません。


2
これは、jimmijの答えの単なる複製です。
maxschlepzig

0

maxschlepzigの受け入れ答え(とで受け入れ答えからの考えに基づき/superuser/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です(タイムアウトのドキュメント(manページ)による)。作成/開かれた場合、終了ステータスは0(成功)です。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.