我有一个系统,其中一个 bash 脚本每隔几秒就在给定目录中创建 zip 文件。我有另一个 bash 脚本,它使用 inotifywait 检查是否出现这些 .zip 文件,并使用 wget 将它们上传到 Amazon S3 存储桶。这看起来通常效果很好。
但是,wget 偶尔会失败,提示 .zip 文件不存在。似乎 inotifywait 正在报告该文件存在,但该文件尚未准备好打开。我使用 close_write
事件来检测文件何时存在。
这是等待文件并上传它们的脚本:
#!/bin/bash
# Watches for new files appearing in a zipfiles directory, and uploads them
# to AWS.
# Process a single .txt file
# $1 = filename
# $2 = the number contained in the filename
process_zip_file( )
{
wget --no-check-certificate \
-O /dev/null \
--method PUT \
--timeout=0 \
--header 'Content-Type: application/zip' \
--body-file=$1 \
https://[redacted AWS endpoint]/${1}
if [[ $? -eq 0 ]]; then
mv $1 $UPLOADED/
fi
}
# Wait for CLOSE_WRITE events in the data directory, and extract the results
# into an array. aline[0] is the path, [1] is the event(s), [2] is the filename
inotifywait -m -e close_write $WATCHDIR | while read -a aline; do
fname=${aline[2]}
# Check it is of the form zip-XYZ.zip where XYZ is a number
if [[ $fname =~ ^zip\-([[:digit:]]*)\.zip$ ]]; then
process_zip_file $fname ${BASH_REMATCH[1]}
fi
done
这是我偶尔收到的错误消息:
--2023-02-09 23:21:25-- https://[redacted AWS endpoint]/zip-00002331.zip
BODY data file 'zip-00002331.zip' missing: No such file or directory
--2023-02-09 23:21:33-- https://[redacted AWS endpoint]/zip-00002332.zip
Resolving [redacted AWS endpoint... [redacted IP addresses]
Connecting to [redacted AWS endpoint]|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 0 [application/json]
Saving to: '/dev/null'
0K 0.00 =0s
0.00 =0s
这显示在 23:21:25 处失败(没有此类文件或目录),随后约 8 秒后成功上传。
谁能解释一下这里发生了什么?
我认为一个解决方法可能是在尝试上传 zip 文件之前先睡一会。但谁能说 1 秒是否是正确的数字呢?而且我真的不想在不了解发生了什么的情况下在管道中插入任意延迟。
操作系统是在 Nvidia Jetson 上运行的 Yocto。内核 4.9.253-l4t-r32.7。文件系统是ext4。
更新:添加有关如何创建 .zip 文件的详细信息。
创建这些 .zip 文件的 bash 脚本本身使用 inotifywait 来检查是否出现 .txt 文件(该文件是从 C++ 程序创建的)。当 .txt 文件出现时,它会生成一个包含 .txt 文件和三个 .jpg 文件的 zip 文件。
#!/bin/bash
# Watches for new files appearing in a data directory, and once a
# .txt file and three image files exist, zips them up into a zip file.
# Process a single .txt file
# $1 = filename
# $2 = the number contained in the filename
process_txt_file( )
{
# Check if photos are all present
if [[ -f pp-${2}-0.jpg && -f pp-${2}-1.jpg && -f pp-${2}-2.jpg ]] ; then
echo "Got all the parts for $2"
# Create the zip file
zip -r zipfiles/zip-${2}.zip \
$1 pp-${2}-0.jpg pp-${2}-1.jpg pp-${2}-2.jpg
if [[ $? -eq 0 ]]; then
rm $1 pp-${2}-0.jpg pp-${2}-1.jpg pp-${2}-2.jpg
fi
else
echo "Not got all the parts for $2"
fi
}
# Wait for CLOSE_WRITE events in the data directory, and extract the results
# into an array. aline[0] is the path, [1] is the event(s), [2] is the filename
inotifywait -m -e close_write $DATA_DIR | while read -a aline; do
fname=${aline[2]}
# Check it is of the form pb-XYZ.txt where XYZ is a number
if [[ $fname =~ ^pb\-([[:digit:]]*)\.txt$ ]]; then
process_txt_file $fname ${BASH_REMATCH[1]}
fi
done
发生错误时该脚本的日志看起来正常:
Got all the parts for 00002330
adding: pb-00002330.txt (deflated 49%)
adding: pp-00002330-0.jpg (deflated 4%)
adding: pp-00002330-1.jpg (deflated 4%)
adding: pp-00002330-2.jpg (deflated 3%)
Got all the parts for 00002331
adding: pb-00002331.txt (deflated 49%)
adding: pp-00002331-0.jpg (deflated 2%)
adding: pp-00002331-1.jpg (deflated 3%)
adding: pp-00002331-2.jpg (deflated 2%)
Got all the parts for 00002332
adding: pb-00002332.txt (deflated 49%)
adding: pp-00002332-0.jpg (deflated 2%)
adding: pp-00002332-1.jpg (deflated 2%)
adding: pp-00002332-2.jpg (deflated 3%)
最佳答案
我想我可能已经找到了答案,通过运行 zip
通过strace
。看来zip
最初创建目标文件,然后立即将其删除。然后它会创建一个包含正确内容的临时文件,并将其重命名为您选择的目标文件名。
我不知道为什么要这样做。例如,使用 zip -r /home/root/foo.zip blah.jpg...
openat(AT_FDCWD, "/home/root/foo.zip", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
close(3) = 0
newfstatat(AT_FDCWD, "/home/root/foo.zip", {st_mode=S_IFREG|0644, st_size=0, ...}, 0) = 0
unlinkat(AT_FDCWD, "/home/root/foo.zip", 0) = 0
openat(AT_FDCWD, "/home/root/ziw022vF", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
// Lots of zipping going on
close(3) = 0
newfstatat(AT_FDCWD, "/home/root/foo.zip", 0x7ff72948b8, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
renameat(AT_FDCWD, "/home/root/ziw022vF", AT_FDCWD, "/home/root/foo.zip") = 0
所以看来我的脚本可以收到文件存在的通知,并尝试在它被 zip
删除期间访问它。 ,以及当临时文件被重命名时。
我将通过在临时文件夹中创建 zip 文件并将它们重命名到主数据文件夹(inotifywait 正在寻找它们的位置)来解决此问题。
正如 @chrslg 有用的评论,解决方案是只监视“moved_to”事件,而不是“close_write”,因为它们会捕获从临时文件到真实 .zip 文件的重命名。
关于linux - inotifywait 可以说文件在可供其他进程访问之前已关闭吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75409252/