我有一个处理一批20MiB CSV文件的脚本,可选择gzip压缩到大约4MiB。有数千个文件,每个文件处理大约需要30秒;读取未压缩文件或压缩文件并解压缩几乎是即时#34;强烈建议可以在流程级别并行化流程。实际上,这是使用复杂的Ruby管道做的事情。但是,我试图使用bash将Ruby代码分解为更小的部分。对于工作控制,我想出了这个bash功能
wait_until_job_available() {
maximum_jobs=${MAXIMUM_JOBS}
[ $# -eq 0 ] || maximum_jobs="${1}"
exit_status=0
RUNNING_JOBS=( $(jobs -p) )
while [ ${maximum_jobs} -le ${#RUNNING_JOBS[@]} ] && [ 0 -eq "${exit_status}" ]
do
# `wait -n` requires bash 4.3 which is unfortunately not available on several recent RHEL-based Linux distributions such as Oracle Linux
wait -n
exit_status=$?
RUNNING_JOBS=( $(jobs -p) )
done
return ${exit_status}
}
这允许我调用wait_until_job_available
,允许可选的最小运行作业数(如果省略,则默认为机器上可用的核心数),然后再保存bash管道。
所以我可以使用它,就像这样:
while read file
do
CAT_COMMAND=cat
# if input file is gzip-compressed, pipe zcat instead of cat
if [ "${INFILE: -3}" == ".gz" ]
then
CAT_COMMAND=zcat
fi
# wait for a job to become available
wait_until_job_available
# read the uncompressed file, write processed data to file.out
process_file -i <(${CAT_COMMAND} ${file}) -o ${file}.out &
# while searching for filesystem paths of type _f_ile
done < <(find ${search_path} -type f)
# wait for all background jobs to finish
wait
如您所见,这应该找到search_path
中的所有文件并将其传递给process_file
命令。在这样做时,我使用进程替换来捕获文件或者在运行时解压缩文件;输入文件名将被一个进程替换,该进程将发出未压缩文件的内容,输出文件是原始文件名,其中包含&#34; .out&#34;追加。 process_file
的调用得到了后盾并被发送到工作控制。看起来很花花公子,对吧?
除非我注意到某些文件处理不当。
我注意到报告由process_file
处理的文件始终报告为/dev/fd/63
,即使是process_file
的单独同时实例也是如此。另一方面,当我将文件单独复制或解压缩为临时文件并将临时文件的名称传递给process_file
时,正常执行并且所有文件似乎都得到了正确处理。
我想避免创建一个临时文件,特别是关于触摸磁盘(性能)和需要在处理后清理(删除)临时文件;有这个问题阻碍了这一点。所以我很好奇是否在替代流程管道的伪文件名称上存在某种竞争条件?或者有什么关于过程替代或工作控制的东西,我似乎误解了?
供参考,我正在使用 Ubuntu Server 14.04,linux 3.19.0-59 Bash 4.3.11 gzip 1.6
答案 0 :(得分:1)
我做了一些挖掘,可能会指出你正确的方向。
显然,/ dev / fd / 63是process_file使用的标准文件描述符。因此,当您运行process_file的多个实例时,它会尝试通过此文件描述符发送所有内容。你可能正在制造冲突或竞争条件。
此页面file descriptors and bash shell scripting和此页面redirection_tutorial包含重定向输出的示例。
您可能需要修改process_file以创建唯一的文件描述符或在使用时锁定描述符。