如何在 shell 脚本中等待文件?

Mo *_*inz 29 shell-script files

我正在尝试编写一个 shell 脚本,该脚本将等待文件出现在/tmp调用的目录中sleep.txt,一旦找到该程序将停止,否则我希望程序处于睡眠(挂起)状态,直到找到该文件. 现在,我假设我将使用测试命令。所以,像

(if [ -f "/tmp/sleep.txt" ]; 
then stop 
   else sleep.)
Run Code Online (Sandbox Code Playgroud)

我是编写 shell 脚本的新手,非常感谢任何帮助!

max*_*zig 40

在 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 ...
Run Code Online (Sandbox Code Playgroud)

(假设 Bash 用于<()输出重定向语法)

与固定时间间隔轮询相比,这种方法的优点是

while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# script execution continues ...
Run Code Online (Sandbox Code Playgroud)

是内核睡得更多。使用 inotify 事件规范,例如create,open脚本只是在/tmp创建或打开文件时安排执行。使用固定时间间隔轮询,您会为每个时间增量浪费 CPU 周期。

当文件已经存在时,我还包含了open注册事件touch /tmp/sleep.txt

  • 只不过这会产生竞争条件 - 与 test -e/sleep 版本不同。无法保证在进入循环之前不会创建文件 - 在这种情况下,事件将被错过。您需要在循环之前添加类似 ( sleep 1; [[ -e /tmp/sleep.txt ]] &amp;&amp; touch /tmp/sleep.txt ; ) &amp; 的内容。当然,这可能会在未来产生问题,具体取决于实际的业务逻辑 (2认同)

jim*_*mij 17

只需将您的测试放入while循环中:

while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# next command
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 10

inotifywait到目前为止给出的一些基于 - 的方法存在一些问题:

  • 他们找不到sleep.txt首先作为临时名称创建然后重命名为sleep.txt. moved_to除了匹配事件之外,还需要匹配事件create
  • 文件名可以包含换行符,打印换行符分隔的文件名不足以确定 asleep.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"
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们正在监视sleep.txt当前目录中的创建,.(因此您将cd /tmp || exit在示例中执行 a before )。当前目录永远不会改变,所以当该管道成功返回时,它是sleep.txt在当前目录中创建的。

当然,你可以./tmp上面的替换,但是在inotifywait运行时,/tmp可以重命名多次(不太可能/tmp,但在一般情况下需要考虑)或安装在其上的新文件系统,因此当管道返回时,它可能不会是一个/tmp/sleep.txt已经创建的而是一个/new-name-for-the-original-tmp/sleep.txt/tmp也可能在间隔中创建了一个新目录,并且不会监视该目录,因此sleep.txt不会检测到在那里创建的目录。