等待脚本中的bash后台作业完成
为了最大限度地利用CPU(我在EC2的Debian Lenny上运行)我有一个简单的脚本来并行地启动作业:
#!/bin/bash for i in apache-200901*.log; do echo "Processing $i ..."; do_something_important; done & for i in apache-200902*.log; do echo "Processing $i ..."; do_something_important; done & for i in apache-200903*.log; do echo "Processing $i ..."; do_something_important; done & for i in apache-200904*.log; do echo "Processing $i ..."; do_something_important; done & ...
我对这个工作解决scheme非常满意,但是我不知道如何编写进一步的代码,只有在所有的循环完成后才执行。
有没有办法控制这个?
有一个bash
内build的命令。
wait [n ...] Wait for each specified process and return its termination sta‐ tus. Each n may be a process ID or a job specification; if a job spec is given, all processes in that job's pipeline are waited for. If n is not given, all currently active child pro‐ cesses are waited for, and the return status is zero. If n specifies a non-existent process or job, the return status is 127. Otherwise, the return status is the exit status of the last process or job waited for.
使用GNU并行将使您的脚本更短,可能更有效:
parallel 'echo "Processing "{}" ..."; do_something_important {}' ::: apache-*.log
这将为每个CPU核心运行一个作业,并继续这样做,直到处理完所有文件。
运行之前,你的解决scheme基本上将作业分成组。 这里有4组32个工作:
当一个完成时,GNU并行会产生一个新的进程 – 保持CPU活动,从而节省时间:
了解更多:
- 观看介绍video快速介绍: https : //www.youtube.com/playlist?list=PL284C9FF2488BC6D1
- 浏览教程(man parallel_tutorial)。 你的命令行会爱你。
我最近不得不这样做,最终得到以下解决scheme:
while true; do wait -n || { code="$?" ([[ $code = "127" ]] && exit 0 || exit "$code") break } done;
这是如何工作的:
wait -n
只要其中一个(可能是很多)后台作业退出就退出。 它总是评估为真,循环继续下去,直到:
- 退出代码
127
:最后一个后台作业成功退出。 在这种情况下,我们忽略退出代码并退出代码为0的子shell。 - 任何后台作业都失败。 我们只是用退出代码退出子shell。
通过set -e
,这将保证脚本将尽早终止,并通过任何失败的后台作业的退出代码。
这是我粗略的解决scheme:
function run_task { cmd=$1 output=$2 concurency=$3 if [ -f ${output}.done ]; then # experiment already run echo "Command already run: $cmd. Found output $output" return fi count=`jobs -p | wc -l` echo "New active task #$count: $cmd > $output" $cmd > $output && touch $output.done & stop=$(($count >= $concurency)) while [ $stop -eq 1 ]; do echo "Waiting for $count worker threads..." sleep 1 count=`jobs -p | wc -l` stop=$(($count > $concurency)) done }
这个想法是用“工作”来看看有多less孩子在后台工作,等到这个数字下降(一个孩子退出)。 一旦有孩子存在,下一个任务就可以开始了。
正如你所看到的,还有一些额外的逻辑可以避免多次运行相同的实验/命令。 它为我完成了这项工作。但是,这个逻辑可以被跳过或进一步改进(例如,检查文件创build时间戳,input参数等)。