简体   繁体   English

bash 脚本在后台运行恒定数量的作业

[英]bash script to run a constant number of jobs in the background

I need a bash script to run some jobs in the background, three jobs at a time.我需要一个 bash 脚本在后台运行一些作业,一次三个作业。

I know can do this in the following way, and for illustration, I will assume the number of jobs is 6:我知道可以通过以下方式做到这一点,为了说明,我假设工作数量为 6:

./j1 &
./j2 &
./j3 &
wait
./j4 &
./j5 &
./j6 &
wait

However, this way, if, for example, j2 takes a lot longer to run that j1 and j3, then, I will be stuck with only one background job running for a long time.但是,这样一来,例如,如果 j2 需要更长的时间来运行 j1 和 j3,那么我将长时间只运行一个后台作业。

The alternative (which is what I want) is that whenever one job is completed, bash should start the next job in the queue so that a rate of 3 jobs at any given time is maintained.另一种选择(这就是我想要的)是,每当一个作业完成时,bash 应该开始队列中的下一个作业,以便在任何给定时间保持 3 个作业的速率。 Is it possible to write a bash script to implement this alternative, possibly using a loop?是否可以编写一个 bash 脚本来实现这个替代方案,可能使用循环? Please note that I need to run far more jobs, and I expect this alternative method to save me a lot of time.请注意,我需要运行更多的工作,并且我希望这种替代方法可以为我节省大量时间。

Here is my draft of the script, which I hope you can help me to verify its correctness and improve it, as I'm new to scripting in bash.这是我的脚本草稿,希望您能帮助我验证它的正确性并改进它,因为我是 bash 中的脚本新手。 The ideas in this script are taken and modified from here , here , and here ):此脚本中的想法是从此处此处此处获取和修改的):

for i in $(seq 6)
do
   # wait here if the number of jobs is 3 (or more)
   while (( (( $(jobs -p | wc -l) )) >= 3 )) 
   do 
      sleep 5      # check again after 5 seconds
   done

   jobs -x ./j$i &
done
wait

IMHO, I think this script does the required behavior.恕我直言,我认为这个脚本会执行所需的行为。 However, I need to know -from bash experts- if I'm doing something wrong or if there is a better way of implementing this idea.但是,我需要从 bash 专家那里知道我做错了什么,或者是否有更好的方法来实现这个想法。

Thank you very much.非常感谢。

With GNU xargs: 使用GNU xargs:

printf '%s\0' j{1..6} | xargs -0 -n1 -P3 sh -c './"$1"' _

With bash (4.x) builtins: 使用bash(4.x)内置:

max_jobs=3; cur_jobs=0
for ((i=0; i<6; i++)); do
  # If true, wait until the next background job finishes to continue.
  ((cur_jobs >= max_jobs)) && wait -n
  # Increment the current number of jobs running.
  ./j"$i" & ((++cur_jobs))
done
wait

Note that the approach relying on builtins has some corner cases -- if you have multiple jobs exiting at the exact same time, a single wait -n can reap several of them, thus effectively consuming multiple slots. 请注意,依赖于内置函数的方法有一些极端情况 - 如果您有多个作业在同一时间退出,则单个wait -n可以获得其中几个,从而有效地消耗多个插槽。 If we wanted to be more robust, we might end up with something like the following: 如果我们想要更强大,我们最终可能会得到以下内容:

max_jobs=3
declare -A cur_jobs=( ) # build an associative array w/ PIDs of jobs we started
for ((i=0; i<6; i++)); do
  if (( ${#cur_jobs[@]} >= max_jobs )); then
    wait -n # wait for at least one job to exit
    # ...and then remove any jobs that aren't running from the table
    for pid in "${!cur_jobs[@]}"; do
      kill -0 "$pid" 2>/dev/null && unset cur_jobs[$pid]
    done
  fi
  ./j"$i" & cur_jobs[$!]=1
done
wait

...which is obviously a lot of work, and still has a minor race. ......这显然是很多工作,但仍然有一个小的种族。 Consider using xargs -P instead. 考虑使用xargs -P代替。 :) :)

Using GNU Parallel: 使用GNU Parallel:

parallel -j3 ::: ./j{1..6}

Or if your shell does not do .. expansion (eg csh): 或者如果你的shell不做..扩展(例如csh):

seq 6 | parallel -j3 ./j'{}'

If you think you cannot install GNU Parallel, please read http://oletange.blogspot.dk/2013/04/why-not-install-gnu-parallel.html and leave a comment on why you cannot install it. 如果您认为无法安装GNU Parallel,请阅读http://oletange.blogspot.dk/2013/04/why-not-install-gnu-parallel.html,并对无法安装的原因发表评论。

Maybe this could assist.. 也许这可以帮助..

Sample usecase : run 'sleep 20' 30 times, just as an example. 示例用例 :运行'睡20'30次,仅作为示例。 It could be any job or another script. 它可以是任何工作或其他脚本。 Our control logic is to keep checking whether "how many already fired?" 我们的控制逻辑是继续检查“已经解雇了多少?” is less than or equal to "max processes defined", inside a while loop. 在while循环中,小于或等于“max processes defined”。 If not, fire one and if yes, sleep .5 seconds. 如果没有,请开一个,如果是,则睡5秒。

Script output: In the below snip, it is observed that now we have 30 'sleep 20' commands running in the background, as we configured max=30. 脚本输出:在下面的片段中,我们观察到现在我们在后台运行30'睡眠20'命令,因为我们配置了max = 30。

%_Host@User> ps -ef|grep 'sleep 20'|grep -v grep|wc -l
30
%_Host@User>

Change value of no. 更改值为no。 of jobs at runtime : Script has a param "max", which takes value from a file "max.txt"( max=$(cat max.txt) ) and then applies it in each iteration of the while loop. 运行时的作业 :脚本有一个参数“max”,它从文件“max.txt”( max=$(cat max.txt) )获取值,然后在while循环的每次迭代中应用它。 As seen below, we changed it to 45 and now we have 45 'sleep 20' commands running in the background. 如下所示,我们将其更改为45,现在我们在后台运行45'睡眠20'命令。 You can put the main script in background and just keep changing the max value inside " max.txt " to control. 您可以将主脚本放在后台,只需将“ max.txt ”中的最大值更改为控件即可。

%_Host@User> cat > max.txt
45
^C
%_Host@User> ps -ef|grep 'sleep 20'|grep -v grep|wc -l
45
%_Host@User>

Script: 脚本:

#!/bin/bash
#---------------------------------------------------------------------#
proc='sleep 20' # Your process or script or anything..
max=$(cat max.txt)  # configure how many jobs do you want
curr=0
#---------------------------------------------------------------------#
while true
do
  curr=$(ps -ef|grep "$proc"|grep -v grep|wc -l); max=$(cat max.txt)
  while [[ $curr -lt $max ]]
        do
    ${proc} &        # Sending process to background.
    max=$(cat max.txt) # After sending one job, again calculate max and curr
    curr=$(ps -ef|grep "$proc"|grep -v grep|wc -l)
  done
  sleep .5    # sleep .5 seconds if reached max jobs.
done
#---------------------------------------------------------------------#

Let us know if it was any useful. 如果它有用,请告诉我们。

This is how I do it:我就是这样做的:

  1. Enable jobs in our script:在我们的脚本中启用作业:

     set -m
  2. Create a trap which kills all jobs if the script is interrupted:创建一个陷阱,如果脚本被中断,它将杀死所有作业:

     trap 'jobs -p | xargs kill 2>/dev/null;' EXIT
  3. Use a loop to start a maximum of 3 jobs in background使用循环在后台启动最多 3 个作业

    for i in $(seq 6); do while [[ $(jobs | wc -l) -ge 3 ]]; do sleep 5 done./j"$i" & done
  4. Finally bring our background jobs back to the foreground:最后将我们的后台作业带回前台:

     while fg >/dev/null 2>&1; do echo -n "" # output nothing done

Because of the last part the script does not exit as long jobs are running and it avoids that jobs get killed by trap .由于最后一部分,脚本不会在作业运行时退出,并且它避免了作业被trap杀死。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM