【问题标题】:Parallel processes: appending outputs to an array in a bash script并行进程:将输出附加到 bash 脚本中的数组
【发布时间】:2018-07-26 17:01:50
【问题描述】:

我有一个 for 循环,其中调用了函数 task。对函数的每次调用都会返回一个附加到数组的字符串。我想并行化这个 for 循环。我尝试使用&,但它似乎不起作用。

这是未并行化的代码。

task (){ sleep 1;echo "hello $1"; }
arr=()

for i in {1..3}; do
    arr+=("$(task $i)")
done

for i in "${arr[@]}"; do
    echo "$i x";
done

输出是:

hello 1 x
hello 2 x
hello 3 x

太棒了!但是现在,当我尝试将它与

[...]
for i in {1..3}; do
    arr+=("$(task $i)")&
done
wait
[...]

输出为空。

【问题讨论】:

    标签: linux bash shell parallel-processing


    【解决方案1】:

    GNU Parallel 擅长并行处理 :-)

    task (){ sleep 1;echo "hello $1"; }
    
    # Make "task" known to sub shells
    export -f task
    
    # Do tasks in parallel
    parallel -k task ::: {1..3}
    

    样本输出

    hello 1
    hello 2
    hello 3
    

    我建议你这样做 - 但查尔斯好心指出这是一个已知的 bash 陷阱:

    array=( $(parallel -k task ::: {1..3}) )
    

    Charles 建议的解决方案是:

    IFS=$'\n' read -r -d '' -a array < <(parallel -k task ::: 1 2 3 && printf '\0')
    

    【讨论】:

    • 执行你的代码会产生zsh:1: command not found: task。这是zsh 特有的问题吗?
    • @BiBi,你用bash 标记了这个,而不是zsh。请为您的设置使用正确的标签。
    • 刚刚添加了zsh 标签。我也对 bash 感兴趣,我打算用 bash 试试你的解决方案。
    • export -f 是一种 bash 主义,它使函数对子 shell 可见
    • IFS=$'\n' read -r -d '' -a array &lt; &lt;(parallel -k task ::: 1 2 3 &amp;&amp; printf '\0') -- 请注意,我停止使用 {1..3} 是因为旧版本的 bash 处理它的方式存在一些错误。 values=( {1..3} ) 然后parallel -k task ::: "${values[@]}" 回避这些问题。
    【解决方案2】:

    尝试将添加步骤封装到函数或临时脚本中,但将项目发送到文件中。我认为您将需要一些我不记得的命令来处理文件锁定。看看你是否也需要导出东西。然后你在最后获取文件。比如:

    echo 'arr=(' > temp
    add() { echo item >> temp; }
    ...
    export -f add
    ...
    add &
    ...
    echo ')' > temp
    source temp
    rm temp
    

    【讨论】:

    • 您将需要更多地填写...s 以获得正确的解决方案——例如,您至少需要等待所有子流程完成。而且您需要更加小心,以免意外截断文件。 (尽管您在此处不需要担心的一件事是文件锁定,只要每个线程以设置了O_APPEND 标志的小于一页长度的单次写入结束)。
    • 我强烈建议在调用完成之前至少成功测试一次答案(复制和粘贴,而不是手动输入,命令之间存在人为长度的延迟)。
    【解决方案3】:

    您正在寻找 parset(自 20170422 起成为 GNU Parallel 的一部分)或 env_parset(自 20171222 起可用):

    # If you have not run:
    #    env_parallel --install
    # and logged in again, then you can instead run this to activate (env_)parset:
    . `which env_parallel.bash`
    
    task (){
      echo "hello $1"
      sleep 1.$1
      perl -e 'print "binary\001\002\n"'
      sleep 1.$1
      echo output of parallel jobs do not mix
    }
    env_parset arr task ::: {1..3}
    env_parset a,b,c task ::: {1..3}
    
    echo "${arr[1]}" | xxd
    echo "$b" | xxd
    

    parset 在 Bash/Ksh/Zsh(包括数组)、ash/dash(不包括数组)中受支持。

    【讨论】:

      【解决方案4】:

      尝试将添加步骤封装到函数或临时脚本中。看看你是否也需要导出东西。比如:

      add() { arr+=(value); }
      ...
      export -f add
      ...
      add &
      

      【讨论】:

      • 这不起作用,因为后台任务是在子进程中运行的;它们不会影响在父进程 shell 中处于活动状态的变量。
      猜你喜欢
      • 2023-03-16
      • 1970-01-01
      • 2012-03-14
      • 2017-08-16
      • 2020-11-17
      • 2023-03-03
      • 2015-08-21
      • 1970-01-01
      • 2017-04-12
      相关资源
      最近更新 更多