使用bash监视目录中预先存在的文件和新文件
我有一个使用inotify工具的脚本使用bash监视目录中预先存在的文件和新文件,bash,unix,inotify,inotifywait,Bash,Unix,Inotify,Inotifywait,我有一个使用inotify工具的脚本 当新文件到达文件夹时,此脚本会发出通知。它对文件执行一些操作,完成后将文件移动到另一个文件夹。(这条线看起来有点像): 使用inotifywait,只能监视新文件。使用路径中的旧文件而不是inotifywait的类似过程将适用于现有文件: for OLDFILE in ${path} do work on/with OLDFILE move NEWFILE no a new directory done 我试着把这两个循环结合起来。首先运
当新文件到达文件夹时,此脚本会发出通知。它对文件执行一些操作,完成后将文件移动到另一个文件夹。(这条线看起来有点像): 使用
inotifywait
,只能监视新文件。使用路径中的旧文件而不是inotifywait
的类似过程将适用于现有文件:
for OLDFILE in ${path}
do
work on/with OLDFILE
move NEWFILE no a new directory
done
我试着把这两个循环结合起来。首先运行第二个循环。但是,如果文件到达的速度很快且数量很大,则在第二个循环运行时,文件将到达。这两个循环都不会捕获这些文件
考虑到文件夹中已经存在文件,并且新文件将很快到达文件夹中,如何确保脚本将捕获所有文件
通过使用inotifywait,只能监视新文件
我想问一下“新文件”的定义。指定事件列表,其中还列出了事件,如create
和delete
和delete_self
,inotifywait还可以查看“旧文件”(定义为在inotifywait执行之前存在的文件)和目录。您只指定了一个事件-e modify
,该事件通知${path}内文件的修改,它包括对先前存在的文件和在执行后创建的文件的修改
。。。如何确保脚本将捕获所有文件
您的脚本足以捕获路径中发生的所有事件。如果无法在生成文件的部件和接收文件的部件之间进行同步,则无法执行任何操作,并且始终存在争用条件。如果脚本接收到0%的CPU时间,而生成文件的部分将获得100%的CPU时间,该怎么办?不保证进程之间的cpu时间(除非使用经认证的实时系统…)。实现它们之间的同步
你可以看其他的节目。如果生成站点在准备好文件时关闭文件,请注意关闭事件。此外,您还可以在后台并行运行workon/with NEWFILE
,以加快执行和读取新文件。但是,如果接收端比发送端慢,如果脚本处理新文件的速度比生成新文件的速度慢,那么您将无能为力
如果文件名中没有特殊字符和空格,我会选择:
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
是在脚本和生成部分之间实现的一些锁定机制。您可以在文件创建过程和文件处理过程之间创建通信
我认为您可以使用一些事务文件系统,这将允许您从其他脚本“锁定”一个目录,直到您准备好处理它,但我在该领域没有经验
我试着把这两个循环结合起来。但是,如果文件到达的速度很快且数量很大,则在第二个循环运行时,文件将到达
在运行进程\u旧文件\u循环之前,在后台运行进程\u新文件\u循环。另外,在继续处理现有文件循环之前,最好确保(即同步)inotifywait已成功启动,以便它们之间也没有竞争条件
也许一个简单的例子和/或起点是:
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不是很快,它是一个shell,应该用来连接两个进程(将一个进程的stdout传递给另一个进程的stdin)并逐行解析流,而IFS=read-r line
在Bash中速度非常慢,通常应该作为最后的手段使用。也许使用xargs
像xargs-P0-n1 sh-c“在$1;mv$1$path上工作”-
或并行
可以加快速度,但一个普通的python或c程序可能要快n倍。一旦inotifywait
启动并等待,它会将消息打印到标准错误。因此,在这一点之后,您需要检查现有文件
因此,一种方法是编写处理标准错误的代码,当它看到该消息时,列出所有现有文件。为了方便起见,您可以将该功能包装到函数中:
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
注:
- 如果您不熟悉我使用的
>(…)
符号,则称之为“流程替换”;有关详细信息,请参阅
- 现在,上面的竞态条件与原始竞态条件相反:如果在
inotifywait
启动后不久创建了一个文件,则列出现有文件并跟随modify
可能会列出它两次。但是,您可以在中轻松地处理该问题,而-loop可以使用if[[-e“$file”]]
在对文件进行操作之前确保该文件仍然存在
- 我有点怀疑你的
inotifywait
选项是否真的是你想要的<代码>修改
,尤其是,似乎是错误的事件。但我相信你可以根据需要调整它们。我在上面所做的唯一更改,除了为了清晰/明确而切换到长选项和为了稳健性而添加--
之外,就是添加--格式%f
,以便获得文件名,而不需要额外的细节
似乎没有任何方法可以告诉inotifywait
使用除换行符以外的分隔符,所以,我就这样做了。确保避免使用包含换行符的文件名
在运行inotifyywait脚本之前,只需将“旧”文件移出即可?@redCricket我想我就是这么做的。问题是文件到达得太快,所以考虑到我已经有了X a
list-existing-and-follow-modify "$path" \
| while IFS= read -r file
# ... work on/with "$file"
# move "$file" to a new directory
done