我需要处理添加到目录中的文件,然后删除它们。文件可能很大(20KiB 到 5 GiB)并且到达缓慢(通过scp
或ftp
)。如何监视路径(可能带有一个systemd.path
单元)并仅在新文件完成写入时才做出反应?我发现我的服务总是在写入文件的第一个字节时触发,从而导致失败,因为完整的文件不存在。
尝试 1 ( DirectoryNotEmpty=
)
我用的是*.path
与DirectoryNotEmpty=
.
优点:
- 看起来像我想要的
缺点:
- 触发太早了。当我处理文件时,文件仍在传输,因此它们不完整(已损坏)。
# /etc/systemd/system/incoming.path
[Path]
DirectoryNotEmpty=/var/incoming
# /etc/systemd/system/incoming.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/incoming
# /usr/local/bin/incoming
for i in /var/incoming/*; do
process $i
rm $i
done
尝试 2(民意调查)
我使用 a*.timer
每分钟触发一次脚本
优点:
- 简单的
缺点:
- 对于小文件,大约 90% 的时间有效;对于大文件,大约 0% 的时间有效。
- 给管道增加最多 1 分钟的延迟
- 日志日志因启动/停止传入设备而混乱
# /etc/systemd/system/incoming.timer
[Timer]
OnCalendar=minutely
# /etc/systemd/system/incoming.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/incoming
# /usr/local/bin/incoming
for i in /var/incoming/*; do
process $i
rm $i
done
尝试 3(民意调查加 md5)
我继续轮询,但我也只处理仅*md5
由客户端在发送主要内容后发送的小文件。这些都小于 1 MTU,所以我希望它们在写入过程中不会被中断。
优点:
- 似乎一直有效(但不确定是否有效)
缺点:
- 给管道增加最多 1 分钟的延迟
- 日志日志因启动/停止传入设备而混乱
- 对于用户来说更复杂。用户脚本需要生成
md5sum
然后发送该文件后发送主文件。
# /etc/systemd/system/incoming.timer
[Timer]
OnCalendar=minutely
# /etc/systemd/system/incoming.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/incoming
# /usr/local/bin/incoming
for i in /var/incoming/*.md5; do
file=$(basename $i .md5)
process $file
rm $file $file.md5
done
答案1
由于唯一的问题是“尝试1”如果文件仍然打开,您可以修改脚本以使用lsof
并sleep
确保文件在处理之前关闭。
像这样的东西:
# /usr/local/bin/incoming
for i in /var/incoming/*; do
while [ "$(lsof $i)" != "" ]; do
sleep 1
done
process $i
rm $i
done
再仔细想想,如果systemd
在已经运行的脚本等待文件关闭时再次调用该脚本,则可能会出现竞争条件。
像这样的事情不太容易因竞争条件而导致错误(仍有改进的空间):
for i in /var/incoming/*; do
while [ -e $i ]; do
if [ "$(lsof $i)" != "" ]
then
sleep 1
else
process $i
rm $i
fi
done
done
答案2
执行此操作的经典方法(例如,在邮件服务器中)是让写入过程写入传入目录,并在完成后将文件移动到处理目录。然后,处理服务可以对到达的新文件采取行动,而无需担心传入的文件仍在写入中。
这确实依赖于能够在上传过程中重命名到位,但可以让您使用DirectoryNotEmpty
您一直尝试使用的功能。