代码之家  ›  专栏  ›  技术社区  ›  user3768495

使用linux shell脚本中的for loop批量处理文件[duplicate]

  •  0
  • user3768495  · 技术社区  · 7 年前

    有没有一种简单的方法来限制bash中并发作业的数量?我的意思是制造&当后台运行的并发作业超过n个时阻止。

    我知道我可以用ps | grep风格的技巧来实现这一点,但有更简单的方法吗?

    0 回复  |  直到 17 年前
        1
  •  2
  •   Tomas M    7 年前

    如果你有GNU并行 http://www.gnu.org/software/parallel/ 你可以这样做:

    parallel gzip ::: *.log
    

    每个CPU核心运行一个gzip,直到所有日志文件都被gzip压缩。

    如果它是更大循环的一部分,您可以使用 sem 相反:

    for i in *.log ; do
        echo $i Do more stuff here
        sem -j+0 gzip $i ";" echo done
    done
    sem --wait
    

    它会做同样的事情,但是给你一个机会为每个文件做更多的事情。

    如果您的发行版没有打包GNU Parallel,您可以通过以下方式安装GNU Parallel:

    $ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
       fetch -o - http://pi.dk/3 ) > install.sh
    $ sha1sum install.sh | grep 883c667e01eed62f975ad28b6d50e22a
    12345678 883c667e 01eed62f 975ad28b 6d50e22a
    $ md5sum install.sh | grep cc21b4c943fd03e93ae1ae49e28573c0
    cc21b4c9 43fd03e9 3ae1ae49 e28573c0
    $ sha512sum install.sh | grep da012ec113b49a54e705f86d51e784ebced224fdf
    79945d9d 250b42a4 2067bb00 99da012e c113b49a 54e705f8 6d51e784 ebced224
    fdff3f52 ca588d64 e75f6033 61bd543f d631f592 2f87ceb2 ab034149 6df84a35
    $ bash install.sh
    

    如果无法在全球范围内安装,它将下载、检查签名并进行个人安装。

    观看GNU Parallel的介绍视频,了解更多信息: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

        2
  •  0
  •   Daniil Iaitskov    6 年前

    一个小bash脚本可以帮助您:

    # content of script exec-async.sh
    joblist=($(jobs -p))
    while (( ${#joblist[*]} >= 3 ))
    do
        sleep 1
        joblist=($(jobs -p))
    done
    $* &
    

    如果你打电话:

    . exec-async.sh sleep 10
    

    ...四次,前三次调用将立即返回,第四次调用将阻塞,直到运行的作业少于三个。

    您需要在当前会话中使用前缀启动该脚本 . 因为 jobs 仅列出当前会话的作业。

    这个 sleep 里面很难看,但我没有找到等待第一份工作终止的方法。

        3
  •  -1
  •   David Callizaya    5 年前

    下面的脚本展示了一种使用函数实现这一点的方法。你可以把 bgxupdate() 和 bgxlimit() 函数,或者将它们保存在一个单独的文件中,该文件源于您的脚本:

    . /path/to/bgx.sh
    

    它的优点是,您可以独立维护多个进程组(例如,您可以运行一个限制为 10 和另一个完全独立的小组 3 ).

    它使用内置的Bash jobs 获取子流程列表,但将其保存在单个变量中。在底部的循环中,您可以看到如何调用 bgxlimit() 功能:

    1. 设置一个空的组变量。
    2. 转给 bgxgrp .
    3. 呼叫 bgxlimit() 使用您想要运行的限制和命令。
    4. 将新组转移回组变量。

    当然,如果你只有一个小组,就使用 bgxgrp 直接变量,而不是传入和传出。

    #!/bin/bash
    
    # bgxupdate - update active processes in a group.
    #   Works by transferring each process to new group
    #   if it is still active.
    # in:  bgxgrp - current group of processes.
    # out: bgxgrp - new group of processes.
    # out: bgxcount - number of processes in new group.
    
    bgxupdate() {
        bgxoldgrp=${bgxgrp}
        bgxgrp=""
        ((bgxcount = 0))
        bgxjobs=" $(jobs -pr | tr '\n' ' ')"
        for bgxpid in ${bgxoldgrp} ; do
            echo "${bgxjobs}" | grep " ${bgxpid} " >/dev/null 2>&1
            if [[ $? -eq 0 ]]; then
                bgxgrp="${bgxgrp} ${bgxpid}"
                ((bgxcount++))
            fi
        done
    }
    
    # bgxlimit - start a sub-process with a limit.
    
    #   Loops, calling bgxupdate until there is a free
    #   slot to run another sub-process. Then runs it
    #   an updates the process group.
    # in:  $1     - the limit on processes.
    # in:  $2+    - the command to run for new process.
    # in:  bgxgrp - the current group of processes.
    # out: bgxgrp - new group of processes
    
    bgxlimit() {
        bgxmax=$1; shift
        bgxupdate
        while [[ ${bgxcount} -ge ${bgxmax} ]]; do
            sleep 1
            bgxupdate
        done
        if [[ "$1" != "-" ]]; then
            $* &
            bgxgrp="${bgxgrp} $!"
        fi
    }
    
    # Test program, create group and run 6 sleeps with
    #   limit of 3.
    
    group1=""
    echo 0 $(date | awk '{print $4}') '[' ${group1} ']'
    echo
    for i in 1 2 3 4 5 6; do
        bgxgrp=${group1}; bgxlimit 3 sleep ${i}0; group1=${bgxgrp}
        echo ${i} $(date | awk '{print $4}') '[' ${group1} ']'
    done
    
    # Wait until all others are finished.
    
    echo
    bgxgrp=${group1}; bgxupdate; group1=${bgxgrp}
    while [[ ${bgxcount} -ne 0 ]]; do
        oldcount=${bgxcount}
        while [[ ${oldcount} -eq ${bgxcount} ]]; do
            sleep 1
            bgxgrp=${group1}; bgxupdate; group1=${bgxgrp}
        done
        echo 9 $(date | awk '{print $4}') '[' ${group1} ']'
    done
    

    下面是一个样本运行,插入空白行以清楚地描绘不同的时间点:

    0 12:38:00 [ ]
    1 12:38:00 [ 3368 ]
    2 12:38:00 [ 3368 5880 ]
    3 12:38:00 [ 3368 5880 2524 ]
    
    4 12:38:10 [ 5880 2524 1560 ]
    
    5 12:38:20 [ 2524 1560 5032 ]
    
    6 12:38:30 [ 1560 5032 5212 ]
    
    9 12:38:50 [ 5032 5212 ]
    
    9 12:39:10 [ 5212 ]
    
    9 12:39:30 [ ]
    
    • 整件事始于 12:38:00 (时间 t = 0 )如你所见,前三个过程会立即运行。
    • 每个进程都会休眠一段时间 10n 秒,第四个进程直到第一个进程退出(在时间 t = 10 ).你可以看到这个过程 3368 以前从名单上消失了 1560 增加了。
    • 同样,第五个过程 5032 什么时候开始 5880 (第二个)在时间点退出 t = 20 .
    • 最后是第六个过程 5212 什么时候开始 2524 (第三个)在时间点退出 t = 30 .
    • 然后,运行开始,第四个过程在此时退出 t = 50 (始于 10 具有 40 持续时间)。
    • 第五个出口 t = 70 (始于 20 具有 50 持续时间)。
    • 最后,第六个出口在时间 t = 90 (始于 30 具有 60 持续时间)。

    或者,如果您更喜欢图形化的时间线形式:

    Process:  1  2  3  4  5  6 
    --------  -  -  -  -  -  -
    12:38:00  ^  ^  ^            1/2/3 start together.
    12:38:10  v  |  |  ^         4 starts when 1 done.
    12:38:20     v  |  |  ^      5 starts when 2 done.
    12:38:30        v  |  |  ^   6 starts when 3 done.
    12:38:40           |  |  |
    12:38:50           v  |  |   4 ends.
    12:39:00              |  |
    12:39:10              v  |   5 ends.
    12:39:20                 |
    12:39:30                 v   6 ends.