大家应该在不少脚本里见过 xargs -P,但要论把“一堆参数”变成“一堆并行任务”这件事做得最通透的,还得是 GNU Parallel。GNU Parallel 的模型很直接:你给它一个输入源(Input source),它把输入源里的每一行(或每个字段)变成一条命令的参数,然后同时跑起来。可很多同学用着用着就卡在“输入源”这一步,搞不清 stdin、-a、::: 到底啥区别,于是命令要么参数全串了,要么干脆不并行。这篇文章专门聊聊输入源这部分,是我在脚本里反复折腾之后的整理,适合已经把 parallel 基础命令跑通、但想更准确控制参数来源的人。
1. 输入源的核心逻辑:先把“参数从哪来”想清楚
1.1 输入源的本质:任务的拆分依据
GNU Parallel 和普通命令执行方式最大的区别,不是“能并行”,而是“怎么拆分任务”。一条命令一次只能处理一组参数,并行则是把一批参数按行拆开,每一行送给一个独立的命令进程。输入源干的事,就是把“这一批参数”准备好,并按行切开递给并行命令。
所以我把输入源理解成一条流水线的传送带:传送带上的每一个“格子”就是一行参数,格子里的内容被塞进命令的占位符里,命令就拿到了一份活儿。这个比喻看着简单,但能帮你在设计脚本时少走很多弯路——你不需要先想“怎么写并行命令”,而是应该先想“输入源怎么组织,才能让每条命令拿到的参数是完整且明确的”。
1.2 三种输入源与适用场景
GNU Parallel 的输入源归结起来就三类,使用上既有重叠又有各自擅长的场景:
| 输入源方式 | 写法 | 典型场景 |
|---|---|---|
| 标准输入(stdin) | 管道喂数据 | 处理另一个命令的输出,比如 find、seq、cat |
| 文件输入 | -a file 或 :::: file | 参数列表已经存在文件里,或需要多次读取 |
| 命令行内嵌 | ::: 参数列表 | 临时写一批参数,不用建临时文件 |
这三类方式可以混合使用,甚至可以同时出现多个输入源,做多参数组合。第4章的重点就是把这些组合方式和它们背后的规则理清,不然看官方文档容易晕。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三种输入源实操:从管道到文件到内嵌参数
2.1 标准输入:最顺手但也最容易忽略细节
GNU Parallel 默认从标准输入读取数据,一行一个参数,这也是最常用的方式。比如批量对一堆数字做处理:
bash复制seq 1 100 | parallel echo "数字是 {}"
这里 {} 是占位符,代表输入源传进来的参数。seq 输出 1 到 100 共 100 行,parallel 就启动 100 个 echo 任务并行执行。你可以用 -j 参数控制同时跑几个,默认是 CPU 核心数,对于简单命令可能看不出差别,但一旦命令里是压缩、转换、网络请求这类耗时操作,并行和串行的差距是数量级的。
标准输入模式下有个细节容易被忽略:默认分隔符是换行,如果输入里有空格,一行内容会被整体当成一个参数吗?答案是会。GNU Parallel 是按行读的,空格不会把一行拆成多个参数。但如果你的数据用的是其他分隔符,比如逗号或制表符,你希望一行里的多个字段各进各的占位符,那就得配合 --colsep 参数,后面我会单独讲。
如果你想每次读入两行合并成一个任务,用 -N2:
bash复制cat pairs.txt | parallel -N2 echo "第一个是 {1},第二个是 {2}"
-N 参数控制“每执行一次命令,从输入源里取几行”,取到的多行分别放到 {1}、{2} 等占位符里。这在处理成对数据时非常实用,比如一个文件里每两行是一组配置,上面是主机名,下面是IP。
2.2 用 -a 指定文件输入:稳定且可复用
标准输入虽然方便,但有一个天然的冲突:如果命令本身还需要读标准输入,管道和命令的标准输入就会打架。这时候用 -a 指定文件更稳。
bash复制parallel -a /tmp/file_list.txt ls -lh {}
-a 后面跟文件名,每行一个参数,效果等价于:
bash复制cat /tmp/file_list.txt | parallel ls -lh {}
但 -a 的好处是:你可以在同一台机器上反复运行同一个命令,不必每次重新生成管道数据,也方便在脚本里做日志排查。配合多个 -a 参数,相当于多个输入源,GNU Parallel 会给它们做笛卡尔积组合:
bash复制parallel -a hosts
