我有以下脚本,用于检查文件夹中是否有文件以及该文件是否稳定(这是针对我收到的大型视频文件)。
#!/bin/bash
cdate1=$(date +%Y%m%d-%T)
folder1="/path-to-folder"
cd $folder1
while file=$(ls "$folder1")
[ -z "$file" ]
do sleep 10
done
echo "There is a file in the folder at $cdate1"
size1=$(stat -c '%s' "$file")
echo "The size1 is $size1"
sleep 30
size2=$(stat -c '%s' "$file")
echo "The size2 is $size2"
if [ $size1 = $size2 ]
then
ls -l
echo "Start converting"
else
echo "Restart the script"
fi
如何修改脚本,以便它可以检查同一文件夹中的多个文件,并在所有文件稳定后启动转换脚本?
答案1
您的脚本中存在许多问题,首先是:
folder1="path-to-folder"
cd $folder1
while file=$(ls "$folder1") ...
这确实是这样cd path-to-folder; ls path-to-folder
;如果path-to-folder
是绝对路径(如您的示例中以“/”开头),这可能有效,但只要您使用相对路径,它就不起作用。如果path-to-folder
包含空格,它也不起作用,因为你应该在任何地方使用引号,例如cd "$folder1"
然后执行 awhile file=$(ls ...)
将执行无限循环,因为file
将始终设置为某物(目录的内容)。
正确的语法是for file in $(ls ...)
,一旦文件名带有空格,它将停止工作(因为循环将在foo
并且bar
如果您有名为 的文件时运行foo bar
)。检查为什么你应该从不解析 ls 的输出。而不是使用ls
你可以简单地做for file in *
。
最后,即使文件大小不再变化,文件也可能会发生变化。
迭代文件的一个好方法是find
命令;检查文件是否发生更改的一个好方法是mtime
文件的 。
以下函数为您提供给定目录中任何文件的最后修改时间 (mtime) 的值
folder1="/path-to-folder"
find "${folder1}" -exec stat -c "%Y" \{\} \; \
| sort -n | tail -1
所以你的脚本可能看起来像:
#!/bin/bash
dir="$1"
# check whether $dir exists
test -d "${dir}" || exit 1
last=0
current=1
while [ "$last" != "$current" ]; do
last=$current
current=$(find "${dir}" -exec stat -c "%Y" \{\} \; \
| sort -n | tail -1)
sleep 10
done
echo "directory is now stable..."
更新
更好的方法是主动通知接收者给定的文件已被传输。一个非常简单的解决方案是在有效负载传输后复制一个空的虚拟文件。例如,对于名为foo.avi
“复制另一个文件”的文件foo.avi.copyfinished
;所以你只需要检查是否存在即可foo.avi.copyfinished
查看是否foo.avi
已准备好。
while true; do
for file_ready in *.copyfinished; do
file=${file_ready%.copyfinished}
if [ -e "${file}.converted" ]; then
echo "skipping already converted file ${file}" 1>&2
else
touch "${file}.converted"
do_convert "${file}"
fi
done
sleep 1
done
这个方案显然需要发送方的配合。
答案2
更容易控制发送端的处理,其中进程知道传输是否完成。这样您也不会收到中止的传输。
将文件发送到临时名称或目录。发送完成后将文件移动到正确的位置
cp srcvideo.avi /folder1/srcvideo.tmp && mv /folder1/srcvideo.tmp /folder1/srcvideo.avi
那么您的脚本不需要对尺寸进行所有测试。它只能等待正确的文件。
cd /folder1 || exit 1
for file in *.avi; do
echo "$file found"
do_some_processing "$file"
done
如果您愿意,您可以通过使用单独的传输目录来实现相同的目的,而无需更改文件名。
答案3
等待文件停止增长是检测下载是否完成的糟糕方法。如果由于网络故障导致下载暂停太长时间,您的脚本将会启动。
下载完成后执行某些操作的最佳方法是指示下载程序在下载完成并成功时运行您的脚本,或者等待下载程序退出,然后在下载成功时运行您的脚本。任何像样的下载程序都至少允许其中一个选项。
如果您无法使用某些较差的下载方法,请使用通知工具在文件下载完成后做出反应,而不是手动查看文件。在 Linux 上,通知工具是inotify。根据下载程序的工作方式,要么对文件关闭做出反应(如果下载程序直接写入最终文件),要么对重命名做出反应(如果下载程序首先写入临时文件,然后将其重命名到位)。
可以使用shell工具inotifywait
当文件事件发生时做出反应。下面是转换每个重命名文件的示例。
cd /path/to/directory
inotifywait -m -e moved_to --format=%f . |
while IFS= read -r filename; do
conversion-program "$filename"
done
答案4
如果您想在知道所有文件是否都存在之前启动脚本,那么首先要检查文件的数量是否恒定。你可以做类似的事情,使用listfile = $(ls "$folder")
[ $( echo $listfile | wc -w) != $(ls "$folder" | wc -w) ]
在 while 循环中;直到事情不相等,你重置$listfile
为$(ls "$folder")
。完成后,您可以简单地执行一个do
循环,在$listfile
其中执行您在一个文件上所做的操作;完成后,您可以启动转换。