Shell脚本并行处理/多线程:优化curl批量拉取文件速度
Got it, let's fix that slow file download issue right away. Parallelizing your curl requests doesn't require fancy multi-threading skills—there are a few straightforward tools and shell tricks you can use to batch your downloads (10 at a time, like you wanted) and cut down that 30-minute wait significantly.
方法1:用xargs(最简便,原生支持)
xargs是大多数Unix/Linux系统自带的工具,能轻松实现并行任务。核心思路是把你的文件URL列表传给xargs,让它同时启动指定数量的curl进程。
步骤:
- 先把所有要拉取的文件URL整理到一个文本文件里,比如
urls.txt,每行一个URL:# 示例urls.txt内容 https://yourapp.com/files/file1.txt https://yourapp.com/files/file2.txt ... https://yourapp.com/files/file100.txt - 用xargs启动10个并行curl进程,每个进程处理一个URL:
xargs -P 10 -n 1 curl -O < urls.txt-P 10:同时运行10个并行进程-n 1:每次给curl传1个URL参数-O:让curl用原文件名保存文件(如果需要自定义文件名,可以结合xargs占位符调整,比如-o "output_{}")
如果需要更精细的控制(比如加超时、重试),可以把curl命令写得更完整:
xargs -P 10 -n 1 curl --connect-timeout 10 --max-time 30 --retry 2 -O < urls.txt
方法2:用GNU Parallel(功能更强大)
如果你的系统没有自带GNU Parallel,可以通过包管理器安装(比如apt install parallel或brew install parallel)。它比xargs更灵活,支持批量处理、进度显示、失败重试等。
基本用法:
从urls.txt读取URL,每次并行10个,保存为原文件名:
parallel -j 10 curl -O {} :::: urls.txt
-j 10:设置并行任务数为10{}:代表从文件中读取的每个URL:::::表示从文件读取输入(如果直接传URL列表用:::)
如果要自定义输出文件名,比如给每个文件加前缀:
parallel -j 10 curl {} -o "downloaded_{/}" :::: urls.txt
这里{/}会提取URL中的文件名部分,最终保存为downloaded_file1.txt这样的格式。
方法3:原生Shell后台进程(无需额外工具)
如果不想安装任何工具,纯Shell脚本就能实现批次并行。思路是在循环中把每个curl放到后台运行,每积累10个就等待所有后台进程完成,再继续下一批。
示例脚本:
#!/bin/bash # 读取URL列表文件 URLS_FILE="urls.txt" # 每批并行数 BATCH_SIZE=10 count=0 while read -r url; do # 启动curl后台进程,自定义保存文件名 filename=$(basename "$url") curl --connect-timeout 10 --max-time 30 "$url" -o "$filename" & count=$((count + 1)) # 每到BATCH_SIZE个进程,等待所有完成 if [ $count -eq $BATCH_SIZE ]; then wait count=0 echo "完成一批下载,继续下一批..." fi done < "$URLS_FILE" # 等待最后一批未完成的进程 wait echo "所有文件下载完成!"
重要注意事项
- 控制并行数:不要把并行数设得太高(比如超过20),否则可能被目标服务器判定为攻击,导致连接被拒绝或限速。先从10开始测试,根据服务器响应调整。
- 添加超时和重试:给curl加
--connect-timeout和--max-time参数,避免单个请求卡住拖慢整个批次;--retry可以让失败的请求自动重试。 - 处理文件名冲突:如果多个URL指向同名文件,要自定义输出文件名(比如加序号或前缀),避免覆盖。
- 日志记录:可以把curl的输出重定向到日志文件,方便排查失败的下载:
curl "$url" -o "$filename" >> download.log 2>&1 &
内容的提问来源于stack exchange,提问作者santosh sm




