【问题标题】:Call shell commands from a list of commands until all commands have been finished从命令列表中调用 shell 命令,直到完成所有命令
【发布时间】:2014-12-15 17:35:37
【问题描述】:

我有我想调用的 shell 命令列表。最多四个进程应同时运行。

我的基本想法是将命令发送到 shell,直到 4 个命令处于活动状态。 然后,该脚本通过查找公共字符串来不断检查所有进程的进程计数,例如"nohup scrapy crawl urlMonitor"。

一旦进程计数低于 4,下一个命令就会发送到 shell,直到所有命令都完成。

有没有办法用 shell 脚本来做到这一点? 我想它会涉及某种无限循环、中断条件以及检查活动进程的方法。不幸的是,我在 shell 脚本方面不是那么好,所以也许有人可以指导我走向正确的方向?

nohup scrapy crawl urlMonitor -a slice=0 &
nohup scrapy crawl urlMonitor -a slice=1 &
nohup scrapy crawl urlMonitor -a slice=2 &
nohup scrapy crawl urlMonitor -a slice=3 &
nohup scrapy crawl urlMonitor -a slice=4 &
nohup scrapy crawl urlMonitor -a slice=5 &
nohup scrapy crawl urlMonitor -a slice=6 &
nohup scrapy crawl urlMonitor -a slice=7 &
nohup scrapy crawl urlMonitor -a slice=8 &
nohup scrapy crawl urlMonitor -a slice=9 &
nohup scrapy crawl urlMonitor -a slice=10 &
nohup scrapy crawl urlMonitor -a slice=11 &
nohup scrapy crawl urlMonitor -a slice=12 &
nohup scrapy crawl urlMonitor -a slice=13 &
nohup scrapy crawl urlMonitor -a slice=14 &
nohup scrapy crawl urlMonitor -a slice=15 &
nohup scrapy crawl urlMonitor -a slice=16 &
nohup scrapy crawl urlMonitor -a slice=17 &
nohup scrapy crawl urlMonitor -a slice=18 &
nohup scrapy crawl urlMonitor -a slice=19 &
nohup scrapy crawl urlMonitor -a slice=20 &
nohup scrapy crawl urlMonitor -a slice=21 &
nohup scrapy crawl urlMonitor -a slice=22 &
nohup scrapy crawl urlMonitor -a slice=23 &
nohup scrapy crawl urlMonitor -a slice=24 &
nohup scrapy crawl urlMonitor -a slice=25 &
nohup scrapy crawl urlMonitor -a slice=26 &
nohup scrapy crawl urlMonitor -a slice=27 &
nohup scrapy crawl urlMonitor -a slice=28 &
nohup scrapy crawl urlMonitor -a slice=29 &
nohup scrapy crawl urlMonitor -a slice=30 &
nohup scrapy crawl urlMonitor -a slice=31 &
nohup scrapy crawl urlMonitor -a slice=32 &
nohup scrapy crawl urlMonitor -a slice=33 &
nohup scrapy crawl urlMonitor -a slice=34 &
nohup scrapy crawl urlMonitor -a slice=35 &
nohup scrapy crawl urlMonitor -a slice=36 &
nohup scrapy crawl urlMonitor -a slice=37 &
nohup scrapy crawl urlMonitor -a slice=38 &

【问题讨论】:

  • 我认为你需要的工具是 GNU parallel;它旨在做你想做的事。

标签: linux bash shell


【解决方案1】:

如果您希望一次连续运行 4 个,请尝试以下操作:

max_procs=4
active_procs=0

for proc_num in {0..38}; do
    nohup your_cmd_here &

    # If we have more than max procs running, wait for one to finish
    if ((active_procs++ >= max_procs)); then
        wait -n
        ((active_procs--))
    fi
done

# Wait for all remaining procs to finish
wait

这是 sputnick 答案的一个变体,可以同时运行 max_procs。一个完成后,它开始下一个。 wait -n 命令等待下一个进程完成,而不是等待所有进程完成。

【讨论】:

    【解决方案2】:

    这是一种通用方法,它始终确保在启动任何其他作业之前少于 4 个作业(但是,如果一条生产线同时启动多个作业,则可能同时有 4 个以上的作业):

    #!/bin/bash
    
    max_nb_jobs=4
    commands_file=$1
    
    while IFS= read -r line; do
       while :; do
          mapfile -t jobs < <(jobs -pr)
          ((${#jobs[@]}<max_nb_jobs)) && break
          wait -n
       done
       eval "$line"
    done < "$commands_file"
    
    wait
    

    将此脚本与您的文件一起用作第一个参数。

    它是如何工作的?对于line 读取的每一行,我们首先通过计算正在运行的作业数(从jobs -pr 获得)来确保运行的少于max_nb_jobs。如果超过max_nb_jobs,我们等待下一个作业终止(wait -n),并再次计算正在运行的作业数。如果有少于max_nb_jobs 运行,我们evalline


    更新

    这是一个类似的脚本,它不使用wait -n。它似乎可以完成这项工作(在 Debian 上使用 Bash 4.2 进行了测试):

    #!/bin/bash
    
    set -m
    
    max_nb_jobs=4
    file_list=$1
    
    sleep_jobs() {
       # This function sleeps until there are less than $1 jobs running
       # Make sure that you have set -m before using this function!
       local n=$1 jobs
       while mapfile -t jobs < <(jobs -pr) && ((${#jobs[@]}>=n)); do
          coproc read
          trap "echo >&${COPROC[1]}; trap '' SIGCHLD" SIGCHLD
          wait $COPROC_PID
       done
    }
    
    while IFS= read -r line; do
       sleep_jobs $max_nb_jobs
       eval "$line"
    done < "$file_list"
    
    wait
    

    【讨论】:

    • 不错的一个!我的 debian 机器不支持 wait -n。有没有办法将 pid 传递给等待函数?
    • @Jabb:对不起,我应该提到wait -n 出现在 Bash 4.3 中(你很可能有 4.2)。没有 4.3,我的回答就没有意义。
    • 完全正确 :-) 我有点卡住了...试图找到通往 GNU 并行的方法,正如在此处发布的 stackoverflow.com/questions/27493205/…
    • 顺便说一句......除此之外,你的解决方案非常酷......从文件中输入命令的好方法
    • 谢谢!我现在使用 GNU Parallel,将尝试尽快测试您的解决方案。最佳答案的功劳归您所有!
    【解决方案3】:

    您可以使用GNU parallel 甚至只是xargs 轻松完成此操作。也就是说:

    declare -i i=0
    while sleep 1; do
        printf 'slice=%d\n' $((i++))
    done | xargs -n1 -P3 nohup scrapy crawl urlMonitor -a
    

    while 循环将永远运行;如果你知道有一个实际的硬限制,你可以做一个for 循环,比如:

    for i in {0..100}…
    

    另外,sleep 1 也很有帮助,因为它可以让 shell 更有效地处理信号。

    【讨论】:

      【解决方案4】:

      尝试这样做:

      for i in {0..38}; do
          nohup scrapy crawl urlMonitor -a slice=$i & _pid=$!
          ((++i%4==0)) && wait $_pid
      done
      

      help wait:

      wait: wait [-n] [id ...]
      Wait for job completion and return exit status.
      
      Waits for each process identified by an ID, which may be a process ID or a
      job specification, and reports its termination status.  If ID is not
      given, waits for all currently active child processes, and the return
      status is zero.  If ID is a a job specification, waits for all processes
      in that job's pipeline.
      
      If the -n option is supplied, waits for the next job to terminate and
      returns its exit status.
      
      Exit Status:
      Returns the status of the last ID; fails if ID is invalid or an invalid
      option is given.
      

      【讨论】:

      • 这将运行 4 个任务,然后等到所有 4 个任务都完成后再开始接下来的 4 个任务。我认为wait -n 命令/选项可能是您应该使用的(以@987654325 为条件) @)。
      • 感谢发现 gniourf_gniourf,POST 已相应编辑
      • 太棒了!谢谢!!试图修改等待条件,但它从所有命令开始... ((i>=4)) && wait -n.. 刚刚看到你的跟进。现在就试试看。
      • the ((i++%4==0)) && wait $_pid 似乎无法正常工作。它最初只启动一个进程。杀死该进程后,将启动 4 个新进程。然后,我需要再杀死 2 个脚本才能再次将队列填充到 4 个。
      • 现在可以了吗? =) 我的测试一次显示 4 个进程
      猜你喜欢
      • 2011-08-06
      • 2012-10-05
      • 2011-02-19
      • 1970-01-01
      • 2011-06-15
      • 2017-08-09
      • 2016-04-14
      • 2015-02-11
      • 2014-04-05
      相关资源
      最近更新 更多