xargs
非常有用,如果您有一个通过命令行参数接受输入的命令,但是如果您有一个通过stdin接受输入的命令,它就没有那么有用了。
对于这种情况,我看到的建议是使用tee
将行复制到几个下游进程,例如:
producer | tee >(consumer0 >out0) >(consumer1 >out1) >(consumer2 >out2) | consumer3 >out3;
cat out* | next-stage-of-pipeline
这具有所有消费者都接受所有生产线的缺点,并且假设consumer{0..3}
是不同的过程。如果我需要每个使用者都相同,并处理部分输入(例如,将顺序使用者并行化),那么它就不能正常工作。
我正在寻找的是类似于xargs
的东西,它允许在同一使用者的多个实例之间拆分工作,然后组合输入并继续进行处理。输出的组合方式应模仿xargs
,因此不能保证行的顺序,但是不会将两行拼接在一起(例如:“ Hello Fred”和“ Hello George”可能以任何顺序出现,但您不会看到“ HHello GeoFregedello”)。
诸如此类的主要用途是处理大量数据,如果对输入的每一行都进行了旋转,则消费者的启动延迟将很明显。如果consumer
的启动费用不高,我只需将其包装在一个小的Shell脚本中,该脚本便将其参数传递到consumer
中,并使用xargs
进行调用。
至少就我所想到的用例而言,生产者将从内部服务中获取大量数据,此后,消费者将需要转换该数据并进行一些API调用。因此,生产者和消费者都将是长时间运行的流程,并且并行运行API调用确实可以加快处理速度。
像这样的东西是理想的,但是我一直找不到能做到这一点的东西:
producer | ??? -P20 consumer | next-stage-of-pipeline
是否有提供此功能的命令行工具?
答案 0 :(得分:1)
在同一使用者的多个实例之间拆分工作
这可以在xargs
的消费者端完成。
例如:
consumer() {
consumer$(($RANDOM % 4)) "$@"
}
export -f consumer
producer | xargs -P20 bash -c consumer --
会选择一个随机的消费者。
该:
consumer() {
seq 4 |
xargs -i{} consumer{} "$@"
}
export -f consumer
producer | xargs -P20 bash -c consumer --
将为每个消费者运行输入。
无论如何,你应该明白这个主意。
对于您的发球台,无需任何临时文件即可轻松实现:
consumer() { sed "s/^/$1: /"; }
producer() { seq 3; }
next-stage-of-pipeline() { sed "s/^/Result: /"; }
producer |
{ tee >(consumer 0 >&10) >(consumer 1 >&11) >(consumer 2 >&12) | consumer 3 >&14 ;} 10>&1 11>&1 12>&1 14>&1 |
next-stage-of-pipeline
如果您需要将每个消费者的输入分成4个,仍然很容易做到:
filter() { awk -vN="$1" '(NR + N) % 4 == 1'
producer |
{ tee >(
filter 1 | consumer 0 >&10
) >(
filter 2 | consumer 1 >&11
) >(
filter 3 | consumer 2 >&12
) |
filter 4 | consumer 3 >&14
;} 10>&1 11>&1 12>&1 14>&1 |
next-stage-of-pipeline
答案 1 :(得分:1)
我认为GNU Paralell
可能会满足您的需求。
producer | parallel consumer | next command in pipeline
编辑:直到开始玩parallel
之前,我完全都不了解这个问题。并行可以将一个函数用作使用者,并且您可以在该函数内部使用流。
例如
consumer ()
{
echo "$@" | awk '{print $2}'
}
export -f consumer
for i in {1..30}; do echo "foo $i bar"; done | parallel consumer
编辑2:
您可以使用--pipe
选项,该选项将数据传输到使用者。 -q
参数将shell引号放在使用者周围:
for i in {1..30}; do echo "foo $i bar"; done | parallel --pipe -q awk '{print $2}'
奖金:
parallel
将在顺序处理器上运行作业。如果您有8个核心和16个使用方,则每个核心将获得2个使用方(可配置)parallel
可以在网络上的多台计算机上运行,只要它可以ssh
对它们运行,而不会提示输入密码(例如,使用ssh-agent
)。答案 2 :(得分:0)
GNU Parallel可以将标准输入分割为同一命令:
... | parallel --pipe consumer
默认情况下,它在\n
上斩波,块大小约为1 MB。可以使用--recstart
/ --recend
和--block
进行更改。
如果输入是文件,则速度更快:
parallel -a bigfile --pipepart --block -1 consumer
这将找到bigfile
的大小,并将其分成每个CPU线程一个块。这是即时完成的-因此不会创建临时文件。
https://zenodo.org/record/1146014的第9章对此进行了详细介绍。
但是,如果您真的希望将整个输入传递给不同的使用者,GNU Parallel也可以这样做:
# Pipe 1..10 to consumer-a..z
seq 10 | parallel --pipe --tee 'echo consumer-{}; cat' ::: {a..z}