GNU Parallel从文件读取输入并拆分后传递至子进程标准输入的正确实现方法
你遇到的问题核心是没搞清楚GNU Parallel中--pipe和--pipe-part的区别,以及-a参数和管道模式的兼容性。我来一步步帮你梳理:
你的尝试为什么不对?
Attempt 1:把拆分内容当成命令参数
parallel --verbose -a <(seq 50) -l 10 -j 5 echo这里
-l 10的作用是让每个echo命令接收10行作为参数,而不是把这10行传给echo的标准输入。所以你会看到echo直接把10个数字打印出来,这和你想要的“子进程从stdin读取分段内容”不符。Attempt 2:
--pipe忽略-a输入导致挂起parallel -a <(seq 50) -l 10 -j 5 --pipe cat--pipe模式下,Parallel默认会从终端/stdin读取数据,完全忽略-a指定的文件。所以这个命令会一直等着你从键盘输入,自然就“挂”住了。管道模式下未正确拆分的问题
你试了:seq 50 | parallel -l 10 -j 5 --pipe wc -l结果输出
50,这是因为wc -l默认会读取整个标准输入的内容并统计行数,而Parallel在处理小数据时的逻辑导致所有内容被传给了同一个进程,没有按预期拆分。
正确的实现方法
如果想要从文件(包括<(seq 50)这种进程替换的临时文件)读取输入,按指定行数拆分后,将每段内容传给子进程的标准输入,你需要用--pipe-part参数,而不是--pipe。--pipe-part专门设计用于从文件读取并拆分数据,每个子进程处理一部分。
方法1:直接使用进程替换文件
parallel --pipe-part -a <(seq 50) -l 10 -j 5 wc -l
这个命令会:
- 从
<(seq 50)生成的临时文件读取50行内容 - 按每10行拆分成5段
- 启动5个
wc -l进程,每个进程的stdin就是这10行内容 - 最终会输出5行
10,完全符合你的预期。
方法2:结合sleep验证并行效果
你之前的测试命令换成--pipe-part后:
parallel --pipe-part -a <(seq 50) -l 10 -j 5 bash -c 'wc -l;sleep 1;'
执行后你会看到5个10几乎同时输出(因为-j 5开启了并行),而不是一个50,完美验证了拆分和并行的效果。
为什么--pipe-part比管道更快?
你提到想从文件读取比bash管道更快,这是对的:--pipe-part可以直接让子进程读取文件的指定偏移段,避免了bash管道的额外数据拷贝开销,处理大文件时优势会更明显。
补充:如果一定要用管道传递文件路径?
如果你坚持要通过管道传递文件路径(比如从其他命令获取文件名),可以用--fifo参数配合,但一般来说--pipe-part是更直接的方案:
echo <(seq 50) | parallel --pipe-part -a {} -l 10 -j 5 wc -l
备注:内容来源于stack exchange,提问作者Martin Mucha

