Answers:
在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 ...
是内核睡眠更多。对于像inotify事件这样create,open
的事件规范,仅在/tmp
创建或打开文件时调度脚本执行。使用固定的时间间隔轮询,您浪费每个时间增量的CPU周期。
我包含了该open
事件,以便touch /tmp/sleep.txt
在文件已经存在时也进行注册。
inotifywait -e create,open --format '%f' --quiet /tmp/sleep.txt --monitor > /dev/null ; # continues once /tmp/sleep.txt is created/opened
呢?
inotifywait
是一个while循环,每次打开/创建文件时都会输出一行。因此,您需要while循环在更改后中断。
sleep.txt
在某个时间点生成的过程之前执行了代码段。因此,没有比赛条件。该问题不包含任何暗示您所设想的方案的信息。
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
当前目录中的创建.
(因此cd /tmp || exit
,在示例中,您需要先进行操作)。当前目录永远不会更改,因此当该管道成功返回时,它是sleep.txt
已创建的当前目录中的目录。
当然,您可以用替换.
为/tmp
,但是在inotifywait
运行时,它/tmp
可能已经被重命名了几次(不太可能用于/tmp
,但是一般情况下需要考虑)或在其上安装了新文件系统,因此当管道返回时,它可能不会是/tmp/sleep.txt
已创建的,但/new-name-for-the-original-tmp/sleep.txt
不是。/tmp
也可以在该间隔中创建一个新目录,并且不会监视该目录,因此sleep.txt
不会检测到该目录中的新目录。
接受的答案确实有效(感谢maxschlepzig),但是将inotifywait监视留在后台,直到脚本退出。如果要由inotifywait监视的目录从点(。)更改为'/ tmp',则唯一与您的要求完全匹配(即等待sleep.txt在/ tmp中显示)的答案似乎是Stephane。
不过,如果你愿意用一个临时目录只为把你sleep.txt标志,并可以打赌,没人会提出任何文件在该目录中,只是要求inotifywait到这个目录看文件的创作就足够了:
第一步:创建要监视的目录:
directoryToPutSleepFile=$(mktemp -d)
第二步:确保目录确实存在
until [ -d $directoryToPutSleepFile ]; do sleep 0.1; done
第三步:等到任何文件显示在里面 $directoryToPutSleepFile
inotifywait -e create --format '%f' --quiet $directoryToPutSleepFile
您将放入的文件$directoryToPutSleepFile
可以命名为sleep.txt awake.txt,无论如何。在脚本中创建任何文件的那一刻$directoryToPutSleepFile
将继续执行该inotifywait
语句。
sleep.txt
在两次inotifywait调用之间创建该文件。
sleep.txt
创建了一个名为的文件。例如尝试touch /tmp/foo /tmp/sleep.txt
,然后inotifywait
将报告的一个实例foo
并退出,到下一次inotifywait启动时,sleep.txt早就已经存在,因此inotifywait将不会检测到它的创建。
通常,要使简单的测试/睡眠循环可靠,就很难做到任何事情。主要问题是测试将与文件创建竞争-除非重复测试,否则会错过create事件。在大多数情况下,这是您最好的选择,在这些情况下,您将使用Shell开始:
#!/bin/bash
while [[ ! -e /tmp/file ]] ; do
sleep 1
done
如果您发现由于性能问题而不够用,那么首先使用Shell可能不是一个好主意。
基于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(根据超时文档(手册页))。如果创建/打开它,则退出状态为0(成功)。
是的,inotifywait以这种方式在子Shell中运行,并且该子Shell仅在发生超时或主脚本退出时(以先到者为准)完成运行。
$MAILPATH
。