并行查询重新运行循环脚本

并行查询重新运行循环脚本

我在 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"

当然,这只是需要做的事情的简单概述。

顺便说一句,这也可以在包装脚本中完成,该脚本除了生成批处理文件然后运行主脚本之外什么也不做。

相关内容