我在 shell 脚本中有以下内容:
for file in $local_dir/myfile.log.*;
do
file_name=$(basename $file);
server_name=$(echo $file_name | cut -f 3 -d '.');
file_location=$(echo $file);
mv $file_location $local_dir/in_progress1.log
mysql -hxxx -P3306 -uxxx -pxxx -e "set @server_name='${server_name}'; source ${sql_script};"
rm $local_dir/in_progress1.log
done
它基本上获取目录中符合条件的所有文件,从文件名中提取服务器名,然后将其传递给 MySQL 脚本进行处理。
我想知道是否有 10 个文件,每个文件需要 60 秒才能完成,5 分钟后我启动 shell 脚本的第二个实例:
- a) 第二个脚本是否仍会看到尚未处理的文件
- b) 如果删除文件,第一次会导致问题吗
或者我可以毫无问题地并行运行它们吗?
答案1
人们会认为“60 秒”(甚至“5 分钟”)只是一个不错的估计,并且存在当第二批开始时第一批仍在进行中的风险。如果您想分隔批次(并且除了偶尔重叠的日志文件之外没有问题),更好的方法是将批次号作为正在进行的文件命名约定的一部分。
像这样的东西:
[[ -s ]] $local_dir/batch || echo 0 > $local_dir/batch
batch=$(echo $local_dir/batch)
expr $batch + 1 >$local_dir/batch
在 for 循环之前,然后在循环开始时,检查您的模式是否与实际文件匹配
[[ -f "$file" ]] || continue
并在文件名中使用批号:
mv $file_location $local_dir/in_progress$batch.log
往复。这降低了碰撞的风险。
答案2
上面有一个答案为该问题提供了一些很好的解决方案,但我想我应该提供一些解释为什么问题是什么。
大多数情况下:只要您重命名的日志文件(正在进行的日志文件)不符合标准,您就可以大概可以安全地运行它最小的风险。但你仍然会遇到一些错误......
您的文件列表是在脚本运行时生成的。所以最终会发生的是:
Script A
获取 的列表10 files
。开始处理,5 files
在(剩余5)中script B
得到一个列表5 remaining files
,开始处理。Script a
然后去处理其列表中的下一个文件(与文件已开始处理的相同script B
),它将出错,因为文件已被重命名。因此,通过错误处理,理论上它可以毫无问题地转到其列表和函数中的下一个。但是,显然总是有机会对齐,但脚本同时击中同一个文件,并且会发生意想不到的事情。随意权衡风险。
一种可能更优雅的解决方案是将其转换为python
脚本,并研究parallel for loops
它允许您创建单个 for 循环,并并行运行它,从而允许一个脚本完成两个或多个脚本的工作。
答案3
另一种方法是在脚本中实现一个简单的批处理队列。
在脚本的开头,您可以执行以下操作:
mkdir -p $localdir/batch
BATCHTMP=$(mktemp batch.XXXXXXXXXX)
MYBATCH="$localdir/batch/batch.$$"
# get list of current log files
find $local_dir/ -name 'myfile.log.*' > "$BATCHTMP"
# exclude any log files already in other batches
grep -vF -f <(sort -u $localdir/batch/batch.*) < "$BATCHTMP" > "$MYBATCH"
rm -f "$BATCHTMP"
# only process log files that are in my batch
for lf in $(cat "$MYBATCH") ; do
....
# somewhere in here, mv or rm the logfile being processed
# so it doesn't get processed again in a later batch run
done
rm -f "$MYBATCH"
当然,这只是需要做的事情的简单概述。
顺便说一句,这也可以在包装脚本中完成,该脚本除了生成批处理文件然后运行主脚本之外什么也不做。