我有一个使用inotify-tool
的脚本。
该脚本通知新文件何时到达文件夹中。它对文件执行一些工作,完成后将文件移到另一个文件夹。 (看起来像这样):
inotifywait -m -e modify "${path}" |
while read NEWFILE
work on/with NEWFILE
move NEWFILE no a new directory
done
使用inotifywait
只能监视新文件。使用for OLDFILE in path
代替inotifywait
的类似过程将适用于现有文件:
for OLDFILE in ${path}
do
work on/with OLDFILE
move NEWFILE no a new directory
done
我尝试结合两个循环。首先运行第二个循环。但是,如果文件快速大量到达,则将发生变化,即第二个循环正在运行时文件将到达。然后,两个循环都不会捕获这些文件。
鉴于文件已经存在于一个文件夹中,并且新文件将快速到达该文件夹内,那么如何确保脚本可以捕获所有文件?
答案 0 :(得分:1)
使用inotifywait,只能监视新文件。
我要定义“新文件”。 man inotifywait指定事件列表,该列表还列出create
和delete
和delete_self
之类的事件,并且inotifywait也可以监视“旧文件”(被定义为存在于文件中的文件以初始化等待执行)和目录。您仅指定了一个事件-e modify
,该事件通知有关$ {path}中文件的修改,它包括对两个既有文件的修改以及在inotify执行之后创建的事件。
...如何确保脚本可以捕获所有文件?
您的脚本足以捕获路径中发生的所有事件。如果在生成文件的部分和接收文件的部分之间没有同步的方法,那么您将无能为力,并且总是有竞争状况。如果脚本获得0%的CPU时间,而生成文件的部分将获得100%的CPU时间怎么办?不能保证进程之间的CPU时间(除非使用经过认证的实时系统...)。实现它们之间的同步。
您可以观看其他事件。如果生成站点准备就绪后会关闭文件,请注意关闭事件。另外,您可以在后台并行运行work on/with NEWFILE
,以加快执行速度和读取新文件。但是,如果接收方的速度比发送方的速度慢,那么如果您的脚本对NEWFILEs的处理速度较慢,则生成新文件的部分将无济于事...
如果文件名中没有特殊字符和空格,我会选择:
inotifywait -m -e modify "${path}" |
while IFS=' ' read -r path event file ;do
lock "${path}"
work on "${path}/${file}"
ex. mv "${path}/${file}" ${new_location}
unlock "${path}"
done
其中lock
和unlock
是在脚本和生成部分之间实现的一些锁定机制。您可以在文件创建过程和文件处理过程之间建立通信。
我认为您可以使用某些事务文件系统,该文件系统使您可以从其他脚本“锁定”目录,直到您准备好进行处理为止,但是我在该领域没有经验。
我尝试结合两个循环。但是,如果文件大量快速到达,则将发生变化,直到第二个循环正在运行。
在运行process_old_files_loop之前,先在后台运行process_new_file_loop。同样,最好确保(即同步)inotifywait已成功启动,然后再继续进行process-existing-files-loop,以便它们之间也没有竞争条件。
也许一个简单的例子和/或起点是:
work() {
local file="$1"
some work "$file"
mv "$file" "$predefiend_path"
}
process_new_files_loop() {
# let's work on modified files in parallel, so that it is faster
trap 'wait' INT
inotifywait -m -e modify "${path}" |
while IFS=' ' read -r path event file ;do
work "${path}/${file}" &
done
}
process_old_files_loop() {
# maybe we should parse in parallel here too?
# maybe export -f work; find "${path} -type f | xargs -P0 -n1 -- bash -c 'work $1' -- ?
find "${path}" -type f |
while IFS= read -r file; do
work "${file}"
done
}
process_new_files_loop &
child=$!
sleep 1
if ! ps -p "$child" >/dev/null 2>&1; then
echo "ERROR running processing-new-file-loop" >&2
exit 1
fi
process_old_files_loop
wait # wait for process_new_file_loop
如果您真的在乎执行速度并希望更快地执行,请更改为python或C(或除shell以外的任何东西)。 Bash并不快,它是一个外壳程序,应该用于互连两个进程(将一个进程的stdout传递到另一个进程的stdin),并且逐行while IFS= read -r line
解析流在bash中非常慢,通常应用作不得已。也许像xargs
或xargs -P0 -n1 sh -c "work on $1; mv $1 $path" --
这样的parallel
可以加快处理速度,但普通的python或C程序可能快n倍。
答案 1 :(得分:1)
inotifywait
启动并等待时,它将打印消息Watches established.
至标准错误。因此,您需要在此之后的 中浏览现有文件。
因此,一种方法是编写将处理标准错误的内容,并在看到该消息时列出所有现有文件。为了方便起见,您可以将该功能包装在一个功能中:
function list-existing-and-follow-modify() {
local path="$1"
inotifywait --monitor \
--event modify \
--format %f \
-- \
"$path" \
2> >( while IFS= read -r line ; do
printf '%s\n' "$line" >&2
if [[ "$line" = 'Watches established.' ]] ; then
for file in "$path"/* ; do
if [[ -e "$file" ]] ; then
basename "$file"
fi
done
break
fi
done
cat >&2
)
}
然后写:
list-existing-and-follow-modify "$path" \
| while IFS= read -r file
# ... work on/with "$file"
# move "$file" to a new directory
done
注意:
>(...)
表示法,则称为“进程替换”;有关详细信息,请参见https://www.gnu.org/software/bash/manual/bash.html#Process-Substitution。inotifywait
启动后不久创建了一个文件,那么list-existing-and-follow-modify
可能会列出两次 。但是您可以通过使用while
来确保在操作之前文件仍然存在,从而轻松地在if [[ -e "$file" ]]
循环中处理该问题。inotifywait
选项确实是您想要的;尤其是modify
似乎是错误的事件。但我相信您可以根据需要进行调整。除了切换为清晰/明确的长选项并添加--
以增强健壮性之外,我上面所做的唯一更改是添加了--format %f
,这样您就可以得到没有多余细节的文件名。inotifywait
使用分隔符,因此,我只是顺其自然。确保避免使用包含换行符的文件名。