我对bash有点陌生,我需要并行运行数百条简短命令,但要顺序打印输出。 该命令将相当短的输出输出到stdout,这是我不想使其松散或与另一个线程的输出混为一谈。 Linux中是否有一种方法可以运行多个命令(例如,并行运行的线程数不超过N个),以便按顺序打印所有命令输出(只要它们不重叠即可)。
当前的bash脚本(此处有完整代码)
declare -a UPDATE_ERRORS
UPDATE_ERRORS=( )
function pull {
git pull # Assumes current dir is set
if [[ $? -ne 0 ]]; then
UPDATE_ERRORS+=("error message")
fi
for f in extensions/*; do
if [[ -d $f ]]; then
########## This code should run in parallel, but output of each thread
########## should be cached and printed sequentially one after another
########## pull function also updates a global var that will be used later
pushd $f > /dev/null
pull
popd > /dev/null
fi
done
if [[ ${#UPDATE_ERRORS[@]} -ne 0 ]]; then
# print errors again
fi
看看gnu.org/software/parallel
谢谢,看起来很有希望,但是如果发生故障,我如何使每个线程向全局数组添加错误消息?
要点1)将-k添加到GNU Parallel的调用中,以保持输出顺序。 要点2)定义一个函数,并确保将其导出,并将该函数传递给GNU Parallel执行-在函数内部,将错误消息附加到数组中。 gnu.org/software/parallel/
您可以为此使用flock。我已经模拟了类似的情况进行测试。 do_the_things proc产生时间重叠输出。在for循环中,文本生成同时调用了几次。输出应该混乱,但是输出将馈送到过程locked_print,该过程等待直到释放锁,然后将接收到的输入输出到stdout。需要导出以从管道内部调用过程。
#!/bin/bash
do_the_things()
{
rand="$((RANDOM % 10))"
sleep $rand
for i in `seq 1 10`; do sleep 1; echo"${rand}-$i"; done
}
locked_print()
{
echo Started
flock -e testlock cat
}
export -f do_the_things
export -f locked_print
for f in a b c d; do
(do_the_things | locked_print) &
done
wait
@ user313294如果有很多事情要做,比如说500,它只会把它们全部扔到CPU上一次完成,不是吗? 有时,当任务竞争CPU /网络/磁盘带宽时,这实际上会使速度变慢。 +1是一个很好,整洁的解决方案。
@MarkSetchell我认为其中的499个将填充其管道缓冲区,并在io上被阻塞,直到释放锁为止,而1将管道输出。
尝试这样的事情。我没有/使用git,所以我做了一个虚拟命令以在我的版本中对其进行仿真。
#!/bin/bash
declare -a ERRORS
ERRORS=( )
function pull {
cd"$1"
echo Starting pull in $1
for i in {0..9}; do echo"$1 Output line $i";done
sleep 5
echo"GITERROR: Dummy error in directory $1"
}
export -f pull
for f in extensions/*; do
if [[ -d $f ]]; then
########## This code should run in parallel, but output of each thread
########## should be cached and printed sequentially one after another
########## pull function also updates a global var that will be used later
echo $f
fi
done | parallel -k pull | tee errors.tmp
IFS=$'
' ERRORS=($(grep"^GITERROR:" errors.tmp))
rm errors.tmp
for i in"${ERRORS[@]}"; do
echo $i
done
您将看到,即使要拉4个目录,整个脚本也只需要5秒钟-尽管执行了4个sleep 5。
最好使用find extensions -type d | parallel -k pull | tee errors.tmp
@ user3132194我只是在可能的情况下重用了OPs代码以演示该技术。 我认为find命令还将找到extensions父目录,而OPs命令则找不到。 实际上,我认为bash4在for d in exrensions*上效果最好,但是我在OSX上没有bash4,正如我所说,我正在集中精力展示GNU Parallel的乐趣:-)
通过添加/列出目录。并行生成一个cd到目录的外壳。如果git pull失败,则会输出魔术字符串。所有输出也作为副本保存在out / 1 / *中。完成所有拉取后,检查魔术字符串出现在哪些文件中,并打印出这些命令的STDOUT / STDERR。清理。
parallel --results out 'cd {} && (git pull || echo e_R_r_O_r)' ::: extensions/*/
grep -l e_R_r_O_r out/*/stdout | parallel 'grep -v e_R_r_O_r {//}/stdout; cat {//}/stderr >&2'
rm -r out