我有一个包含多个管道的脚本,我想对其进行并行化。目前看起来是这样的:
result1=$(pipeline | number | one)
result2=$(pipeline | number | two)
result3=$(pipeline | number | three)
printf 'Result 1: %s\n' "$result1"
printf 'Result 2: %s\n' "$result2"
printf 'Result 3: %s\n' "$result3"
我想做这个:
result1=$(pipeline | number | one) &
result2=$(pipeline | number | two) &
result3=$(pipeline | number | three) &
printf 'Result 1: %s\n' "$result1"
printf 'Result 2: %s\n' "$result2"
printf 'Result 3: %s\n' "$result3"
但这不起作用,因为分配是在子 shell 中执行的。
我可以重定向磁盘上的输出文件:
pipeline | number | one > result1 &
pipeline | number | two > result2 &
pipeline | number | three > result3 &
wait
printf 'Result 1: %s\n' "$(<result1)"
printf 'Result 2: %s\n' "$(<result2)"
printf 'Result 3: %s\n' "$(<result3)"
但我也想避免创建临时文件。
我也尝试过使用read
and进行各种操作coproc
,但问题似乎总是在于read
orvar=$()
会阻塞。
有没有办法让 bash 异步捕获多个命令的输出而不创建任何临时文件?
答案1
您正在寻找parset
(GNU Parallel 的一部分)。
parset result1,result2,result3 'pipeline | number | {}' ::: one two three
parset
构建于 GNU Parallel 之上,GNU Parallel 已经经过多年的现场测试。因此,如果您的输出包含 \n、空格、TAB、* 或类似内容,您不太可能遇到一些令人讨厌的令人惊讶的竞争条件(这种情况只会偶尔发生一次),并且不太可能遇到问题。
示例1:
. env_parallel.bash
env_parallel --session
pipeline() { seq 100; }
number() { perl -pe 's/^/Foo: /'; }
one() { grep 1; }
two() { grep 2; }
three() { grep 3; }
env_parset result1,result2,result3 'pipeline | number | {}' ::: one two three
env_parallel --end-session
echo "$result3"
示例2:
. env_parallel.bash
pipeline() { seq 100; }
number() { perl -pe 's/^/Foo: /'; }
one() { grep 1; }
two() { grep 2; }
three() { grep 3; }
export -f pipeline number one two three
parset result1,result2,result3 'pipeline | number | {}' ::: one two three
echo "$result3"
(GNU 并行做创建临时文件,但它会尽力清理这些文件 - 即使发生意外崩溃)。
答案2
将运行三个命令。我们不知道哪个会是掉队者。
你(粗略地)说过你希望这一切发生。
one &
two &
three &
wait
问题是,这会产生三行输出,所有输出都以随机顺序混杂在一起。
这听起来不像是一个难题。只需标记每个输出行即可。
( one | sed -l 's/^/1 /' &
two | sed -l 's/^/2 /' &
three | sed -l 's/^/3 /' &
) |
sort -nk1 --stable |
sed 's/^[123] //'
现在您需要解析三行输出,它们可以方便地按正确的顺序排列。根据需要将它们分配给变量。