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

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

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

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

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

答案1

在Linux下,您可以使用inotify内核子系统有效地等待目录中文件的出现:

inotifywait  -e create,moved_to,attrib --include '/sleep.txt$' -qq /tmp
# script execution continues ...

注意:我添加了该attrib事件,以便touch /tmp/sleep.txt在文件已存在时也进行注册。


如果 sleep.txt 显示与inotify等待调用,即在手表建立之前可能创建或触摸文件时 - 然后再也不会,之后 - 人们可以像这样扩展代码:

coproc inw {
    LC_ALL=C inotifywait  -e create,moved_to --include '/sleep.txt$'  /tmp 2>&1
}
while IFS= read -r -u "${inw[0]}" line; do
    if [ "$line" = "Watches established." ]; then
        break
    fi
done
if [ -f /tmp/sleep.txt ]; then
    kill $inw_PID
else
    wait
fi
# script execution continues ...

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

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

是你的进程休眠更多。使用 inotify 事件规范(例如,脚本)仅在创建或打开create,attrib下面的文件时安排执行。/tmp通过固定时间间隔轮询,您会浪费每个时间增量的 CPU 周期。

答案2

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

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

答案3

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

  • 他们无法找到sleep.txt首先作为临时名称创建然后重命名为sleep.txt.moved_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请注意,我们正在监视当前目录中的创建(因此您需要在示例中.执行 before 操作)。cd /tmp || exit当前目录永远不会改变,因此当该管道成功返回时,它就是sleep.txt已创建的当前目录。

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

答案4

基于 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 以这种方式在子 shell 中运行,并且该子 shell 仅在发生超时或主脚本退出时(以先到者为准)才会完成运行。

相关内容