GNU Parallel能否分组stdout/stderr之外的文件描述符输出
问题描述
我有一个简单的bash脚本 curlit.sh(简化版本):
# stdout和stderr用于输出其他内容 echo "hello" >&2 echo "world" # curl分别将响应结果、响应头输出到文件描述符3和4 curl --output > >(cat >&3) --dump-header > >(cat >&4) "$1"
现在需要对上百万个域名并行运行该脚本,执行命令如下:
cat MillionDomains.out | parallel -j0 ./curlit.sh {} > stdout.out 2> stderr.out 3> html.out 4> header.out
当前GNU Parallel默认可以对stdout.out和stderr.out的输出做分组缓冲(单个任务的输出完整缓存后再统一写入,避免不同任务输出交错),但html.out和header.out的输出存在不同任务内容交错混杂的问题,无法正常使用。
是否可以配置GNU Parallel,使其同样支持对fd 3和fd 4的输出做分组,即按单进程缓冲后再统一输出对应内容?
解决方案
方案1:新版本原生支持(版本≥20190722)
2019年7月之后发布的GNU Parallel版本已经支持对自定义文件描述符做分组缓冲,只需要通过--fifo参数声明要接管的额外文件描述符即可,调整后命令如下:
cat MillionDomains.out | parallel -j0 --group --fifo 3 --fifo 4 ./curlit.sh {} > stdout.out 2> stderr.out 3> html.out 4> header.out
执行前先通过parallel --version确认版本号,版本达标即可直接使用,不会产生额外临时文件。
方案2:独立临时文件方案(全版本兼容,100%无交错)
如果使用的是旧版本Parallel,或者担心原生方案存在兼容问题,用这个方案最稳妥,从根源避免输出交错:
- 先创建临时目录存放单任务输出:
mkdir -p ./tmp_out/{stdout,stderr,html,header}
- 调整Parallel执行逻辑,每个任务的四类输出单独写入对应目录下的独立文件,文件名直接用域名做标识。如果域名包含
/等不适合做文件名的特殊字符,可以提前做转义处理,或者用行号作为文件名避免异常。
cat MillionDomains.out | parallel -j0 ' ./curlit.sh {} \ > ./tmp_out/stdout/{} \ 2> ./tmp_out/stderr/{} \ 3> ./tmp_out/html/{} \ 4> ./tmp_out/header/{} '
- 所有任务执行完成后,按照原始域名列表的顺序,把单任务文件合并成最终的总输出文件:
# 合并stdout while read -r domain; do cat "./tmp_out/stdout/$domain" done < MillionDomains.out > stdout.out # 合并stderr while read -r domain; do cat "./tmp_out/stderr/$domain" done < MillionDomains.out > stderr.out # 合并html内容 while read -r domain; do cat "./tmp_out/html/$domain" done < MillionDomains.out > html.out # 合并header内容 while read -r domain; do cat "./tmp_out/header/$domain" done < MillionDomains.out > header.out
合并完成确认内容无误后,直接删除临时目录即可:
rm -rf ./tmp_out
这个方案额外支持断点续跑:如果任务中途中断,再次执行前过滤掉已经生成对应输出文件的域名即可,不用重复发起请求,适合百万级域名的大批量跑数场景。
注意:百万级文件需要确保文件系统有足够的inode剩余,执行前可以通过
df -i查看inode使用情况,普通ext4/xfs文件系统默认配置完全可以承载这个量级的文件数。
内容的提问来源于stack exchange,提问作者Yan Foto
相关产品推荐
相关产品推荐

