我有 shell 脚本,它等待一些文件从远程计算机到达,当它到达时,它将它们捕获到一个新文件。为此,我使用 while 循环,如下所示:
while true
do
if [ $(find ../Test_Data/local_enc* | wc -l) -eq 2 ]
then
break
else
sleep 0.001
fi
done
cat ../Test_Data/local_enc* > ../Test_Data/All_Enc_Coords.txt
问题是 All_Enc_Coords 文件有时被写入,有时不被写入。我认为这是因为,即使文件中没有数据,find 函数也会返回一些值。
如何保证文件写入成功?以及如何在 while 循环中指定它。这意味着,我希望所有文件都被完全写入,然后对它们进行cat?..我在cat之前使用了sleep 1,发现文件已完全写入,但是有什么方法可以检查这一点吗?
答案1
您能否让远程计算机在开始上传文件之前创建一个文件,然后将其删除?
例如使用 ssh(类似可以使用 ftp 或 HTTP PUT 完成):
ssh yourhost touch ../Test_Data/upload-in-progress
scp local_enc* yourhost:../Test_Data/
ssh yourhost rm ../Test_Data/upload-in-progress
然后您的脚本所要做的就是等待正在上传的文件消失。这可以通过睡眠循环来完成,或者可以使用包inotifywait
中的内容来完成inotify-tools
。
注意:如果远程主机在完成上传之前死亡或其脚本被终止,它将留下一个陈旧的上传中文件。在我看来,这是一个比尝试猜测上传何时完成而产生竞争条件的风险要小得多的问题(因为仅在目标计算机上运行的所有解决方案都容易出现)
我最初考虑使用lsof | grep local_enc | wc -l
,但这和你的 一样容易出现竞争find .. | wc -l
。
与使用inotify
或类似的方式获取 Test_Data 目录更改的通知 - 您可以知道该目录中文件何时创建/更改,但不会告诉您上传会话何时完成...但是 inotify 与信号量结合使用文件会起作用。 inotify 等待 Test_Data 目录更改,然后 inotify 休眠,直到删除正在进行的上传。
此外,如果远程主机上的上传脚本从 cron 运行,过时的上传中文件将在下次运行时自行修复。或者,您的脚本可以编写为假设任何早于 X 分钟的上传文件已过时,应删除/忽略(inotifywait
有一个-t
或--timeout
选项在这里很有用),但网络延迟或临时中断可能会导致您这里出现问题。
答案2
有两种方法可以实现它。
检查文件写入后2或3分钟内是否没有被触摸。这样,您就可以判断该文件是否已完全写入。要检查文件是否至少在 3 分钟前写入:
find /testfolder/filename.* -type f -mmin +3
如果您有多个文件,则可以使用 for 循环:
for f in $(find /testfolder/filename.* -type f -mmin +3) do mv filename.* to destination cat ../Test_Data/local_enc* > ../Test_Data/All_Enc_Coords.txt done
如果文件中有预告片,那么您可以读取预告片记录,然后决定何时对文件进行编目。
答案3
您可以使用测试用例。
while [[ ! -e ../Test_Data/All_Enc_Coords.txt ]]; do
if [ $(find ../Test_Data/local_enc* | wc -l) -eq 2 ]; then
cat ../Test_Data/local_enc* > ../Test_Data/All_Enc_Coords.txt
else
sleep 0.001
fi
done
根据评论,如果您在写入之前检查包含数据的文件,您可以使用:
while [[ ! -s ../Test_Data/All_Enc_Coords.txt ]]; do
if [ $(find ../Test_Data/local_enc* | wc -l) -eq 2 ]; then
cat ../Test_Data/local_enc* > ../Test_Data/All_Enc_Coords.txt
else
sleep 0.001
fi
done
答案4
我正在使用下面的代码来检查数据是否仍在文件中。如果没有的话就写完整了。循环退出并开始下一个处理。
for (( ; ; ))
do
bfr=$(stat -c%s "$f")
sleep 0.5
aftr=$(stat -c%s "$f")
if [ $bfr -eq $aftr ];
then
break;
fi
done