【问题标题】:bash script for many files in parallel并行处理多个文件的 bash 脚本
【发布时间】:2015-11-25 01:43:09
【问题描述】:

我已阅读有关此主题的类似问题,但没有一个可以帮助我解决以下问题:

我有一个如下所示的 bash 脚本:

#!/bin/bash

for filename  in /home/user/Desktop/emak/*.fa; do
    mkdir ${filename%.*}
    cd ${filename%.*}
    mkdir emak
    cd ..
done

这个脚本基本上做了以下事情:

  • 遍历目录中的所有文件
  • 使用每个文件的名称创建一个新目录
  • 进入新文件并创建一个名为“emak”的新文件

真正的任务比创建“emak”文件的计算成本高得多......

我有大约数千个文件要遍历。 由于每次迭代都独立于前一次,我会喜欢 将其拆分为不同的处理器(我有 24 个内核),这样我就可以同时处理多个文件。

我阅读了一些关于并行运行(使用:GNU)的先前帖子,但我没有看到在这种情况下应用它的明确方法。

谢谢

【问题讨论】:

  • 您自己尝试过使用 GNU 并行吗?很高兴看到这一点。
  • parallel -j $((getconf _NPROCESSORS_ONLN-1))
  • 顺便说一句,通过shellcheck.net 运行您的代码以自动发现引用错误,因此我们无需在此处指出它们。 (如果您的文件名中有空格,则当前代码的行为会很糟糕)。
  • @rai 默认为核心数。 -j-1 == 核心数减一。

标签: bash parallel-processing


【解决方案1】:

不需要parallel;你可以简单地使用

N=10
for filename in /home/user/Desktop/emak/*.fa; do
    mkdir -p "${filename%.*}/emak" &
    (( ++count % N == 0)) && wait
done

第二行暂停每第 N 个作业,以允许所有先前的作业完成,然后再继续。

【讨论】:

  • 不错。此外,它比 GNU 并行启动新 shell 实例的方法效率更高。
  • ...虽然确实如此,减少单个mkdir 调用的数量将进一步提高性能。也许有人想通过管道输入xargs -0 -P 0 mkdir -p?这也避免了浪费的 CPU 在开始新批次之前等待所有 N 个进程完成。
  • 我开始研究类似find ... -exec mkdir -p {} + 的东西,但对如何将其与从filename 剥离.fa 结合起来失去了兴趣。免费代表任何想要追求的人! :)
  • -exec bash -c 'mkdir -p "${@%.*}"' {} +,也许?
  • 去掉.fa,但不添加/emak
【解决方案2】:

类似这样的 GNU Parallel,您可以创建和导出一个名为 doit 的 bash 函数:

#!/bin/bash

doit() {
    dir=${1%.*}
    mkdir "$dir"
    cd "$dir"
    mkdir emak
}
export -f doit
parallel doit ::: /home/user/Desktop/emak/*.fa

如果您的“计算成本高” 部分花费的时间更长,或者尤其是变量,您将真正看到这种方法的好处。如果需要,比如说最多 10 秒并且是可变的,GNU Parallel 将在 N 个并行进程中最短的一个完成后立即提交下一个作业,而不是在开始下一批 N 个作业之前等待所有 N 个完成。

作为一个粗略的基准,这需要 58 秒:

#!/bin/bash

doit() {
   echo $1
   # Sleep up to 10 seconds
   sleep $((RANDOM*11/32768))
}
export -f doit
parallel -j 10 doit ::: {0..99}

这是直接可比的,需要 87 秒:

#!/bin/bash
N=10
for i in {0..99}; do
    echo $i
    sleep $((RANDOM*11/32768)) &
    (( ++count % N == 0)) && wait
done

【讨论】:

  • 当然,尽管在这种特殊情况下,我认为启动一个新的子进程 shell 以在每个目录中运行此函数的每个副本的开销将远远超过时间由并行化本身保存。
  • @CharlesDuffy OP 说实际过程“计算成本要高得多”
  • 叹息。我希望人们会在他们的示例中添加sleep 3 # do something expensive here 来演示这种事情。
  • 这个真的很好用!!每次迭代需要 47 秒。使用 doit 函数执行 24 次迭代需要 50 秒。我尝试了 48 个文件(48 次迭代),需要 100 秒。它以 24 个块为单位工作,在我看来,它之所以这样做是因为我有 24 个内核。我对吗?非常感谢!
  • 正确!例如,您还可以使用parallel --eta 来估计它何时完成(估计到达时间)和parallel -j 16 以在 16 个内核上运行。此外,如果您有多个服务器可用,您只需将它们添加到命令行就可以在多台机器上分发作业 - 查看任何 GNU Parallel 教程。为了公平对待@chepner,您应该将他的N=10 更改为N=24
猜你喜欢
  • 1970-01-01
  • 2016-07-17
  • 2017-12-04
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
相关资源
最近更新 更多