Bash:等待固定数量的进程

Gio*_*ano 6 bash jobs background-process

我写了一个 bash 脚本,它执行一个 Java jar n次。

在实践中,我定义了 e foo()函数,包含对jar的调用,然后我运行这个脚本:

for RUN in $(seq 1 $RUNS) 
do 
    foo & 
done
Run Code Online (Sandbox Code Playgroud)

现在,我不想在jar中并行执行运行时间。等待限制并行执行的数量(例如每 10 个进程)?wait

小智 5

bash4.4 版引入了一个有用的新习语,称为参数转换,可以在这种情况下为您提供帮助。在下面的代码片段中,请注意${num_jobs@P}. 的@P是一种类型的参数变换使得就好像它是一个变量被扩展bash提示字符串。有关man bash其他参数转换选项,请参阅。

#!/bin/bash
num_procs=$1
num_iters=$2
num_jobs="\j"  # The prompt escape for number of jobs currently running
for ((i=0; i<num_iters; i++)); do
  while (( ${num_jobs@P} >= num_procs )); do
    wait -n
  done
  foo &
done
Run Code Online (Sandbox Code Playgroud)

/sf/answers/2714305961/ 上感谢 chepner 。


按照 Kusalananda 的评论,如果需要,为了使这组进程独立于会影响计数的任何其他后台作业,您可以用它们自己的 shell 包装它们。为此,需要进行一些更改。

#!/bin/bash
# start a wrapper shell for the group of jobs
cat<<EOS | bash &
num_procs="$1"
num_iters="$2"
for ((i=0; i<num_iters; i++)); do
  # escape what's not supposed to be expanded
  # at the time of here-doc redirection
  while (( \${num_jobs@P} >= num_procs )); do  
    wait -n
  done
  foo &
done
EOS
# now you can do other things
Run Code Online (Sandbox Code Playgroud)


Kus*_*nda 3

for RUN in $(seq 1 $RUNS); do
    foo &

    if (( (RUN % 10) == 0 )); then
        wait
    fi
done
Run Code Online (Sandbox Code Playgroud)

或者,使用替代循环构造(恕我直言,看起来更好):

for (( r = 1; r <= RUNS; ++i )); do
    foo &

    if (( (r % 10) == 0 )); then
        wait
    fi
done
Run Code Online (Sandbox Code Playgroud)

如果不是 10 的倍数,您可能还需要wait在循环后添加一个 lone 。$RUNS


除了RUNS运行总数之外,我们还可以想象有n10 个作业的批次:

for (( i = 0; i < n; ++i )); do
    printf 'starting batch %d...\n' "$i"
    for (( j = 0; j < 10; ++j )); do
        foo &
    done

    echo 'waiting...'
    wait
done
Run Code Online (Sandbox Code Playgroud)

使用xargs并且没有显式的替代解决方案wait

seq 1 "$RUNS" | xargs -n 1 -P 10 foo
Run Code Online (Sandbox Code Playgroud)

然而,这会给进程一个可能不需要的foo命令行参数(由 生成的整数之一)。seq这消除了这个问题:

seq 1 "$RUNS" | xargs -n 1 -P 10 sh -c 'foo'
Run Code Online (Sandbox Code Playgroud)