← 中文资源

简体中文版本

用 xargs -P 并行化 shell 任务并保住顺序

用 xargs -P 把独立命令扇出到多核,保持输出可归属,再按输入顺序重组结果。

阅读英文原文

目标

独立的逐项工作是最好做的并行,而 xargs -P 交付它不需要新语言、不需要任务队列、不需要 worker 框架。两条纪律:并发数有上界;每项写各自的输出文件,完成顺序永远不会污染结果顺序。一条 50 分钟的 for 循环可以就此变成 6 分钟。

步骤要点

  1. 先确认工作 embarrassingly parallel:每项只读写自己的文件,没有共享索引或累加状态。
  2. 用 ls 管道接 xargs -P “$(nproc)” -I{} 扇出,每项输出写到独立文件,输出计数应等于输入计数。
  3. 需要合并流式结果时,让每项写 results/$n.html 与 .err 附属文件,最后按输入顺序 cat 重组,失败项留名不中断批次。
  4. 对两个并发档位各做一次 time 对比:CPU 密集型 16 胜 4,IO 密集型打平或反转,据此决定参数而非拍脑袋。
  5. 文件名可能含空格时改用 find -print0 接 xargs -0,内层脚本用 _ 占位拿干净的 $1。

排错提示

子进程共写 stdout 会交错成乱码,并行子项一律写独立文件事后拼接,或直接用 parallel –results 目录结构;单项失败中止了全部,说明把 set -e 塞进了扇出层,xargs 本身遇错返回 123 但会跑完其余项,把退出码收进 .err 文件最后统一决策;满并发时机器卡死是内存或 IO 瓶颈,把 -P 降到核数一半,或给昂贵资源加锁只并行计算部分。

适用边界

xargs -P 没有工作窃取,任务成本不均时尾部会闲着核,GNU parallel 的分片可以缓解;每项输出文件消耗与项数成正比的磁盘,输出小且不在乎顺序时才考虑流式。

代码示例

# before: sequential, 50 minutes
for f in docs/*.md; do pandoc "$f" -o "build/$(basename "$f" .md).html"; done

# after: bounded parallel, 6 minutes
printf "%s\n" docs/*.md | xargs -P "$(nproc)" -I{} bash -c '
  pandoc "{}" -o "build/$(basename "{}" .md).html" 2> "build/$(basename "{}" .md).err" || true
'

# ordered recombination afterwards
for f in docs/*.md; do cat "build/$(basename "$f" .md).html"; done > combined.html