高效追加大文件的方法:优化慢执行的Shell脚本求助
优化大文件处理Shell脚本的执行速度
这个问题我太熟了——用bash逐行处理大文件时,最容易踩的坑就是在循环里频繁调用外部命令,每一次echo、grep、cut都是开一个新进程,大文件几万几十万行下来,进程开销直接拉满,速度能快才怪!
下面给你两种优化方案,从易到难,按需选择:
方案一:用Bash内置操作替代外部命令(保留原有循环逻辑)
把所有依赖外部工具的子串操作换成bash原生的字符串处理,完全避免子进程开销:
#!/bin/bash array=() # 逐行读取文件,直接关联输入文件避免冗余操作 while IFS='' read -r line || [[ -n "$line" ]]; do # 用bash内置匹配替代grep,检查行中是否包含"POSITION" if [[ "$line" == *"POSITION"* ]]; then # 用参数展开提取括号内的内容,替代两次cut temp="${line#*POSITION(}" # 删除"POSITION("左侧的所有内容 temp="${temp%)*}" # 删除")"右侧的所有内容 # 拆分冒号分隔的前两部分,替代第三次cut IFS=':' read -r part1 part2 _ <<< "$temp" coOrdinates="$part1:$part2" # 检查内容是否为空(去除空格后) if [[ -n "${coOrdinates// }" ]]; then array+=("$coOrdinates") else echo "Not adding" fi else echo "Not adding" fi done < your_input_file.txt # 最终将数组内容输出到目标文件 printf "%s\n" "${array[@]}" > output.txt
提速原因:
- 全程使用bash内置的参数展开和字符串匹配,没有启动任何外部进程
- 把
grep的匹配逻辑换成[[ "$line" == *"POSITION"* ]],完全在bash内部完成 - 用
${var#*pattern}和${var%pattern*}替代cut,直接截取子串,比调用外部工具快数倍
方案二:用Awk一次性处理(大文件最优解)
如果文件是百万行甚至GB级别的,bash循环哪怕优化后还是不如awk高效——因为awk是专门为文本处理设计的,单进程处理所有行,没有循环内的进程调度开销:
#!/usr/bin/awk -f /POSITION/ { # 用正则提取POSITION(...)中的内容 match($0, /POSITION\(([^)]+)\)/, match_result) if (match_result[1] != "") { # 拆分冒号分隔的前两部分 split(match_result[1], parts, ":") if (parts[1] != "" && parts[2] != "") { coord = parts[1] ":" parts[2] # 去除内容中的空格并检查是否为空 gsub(/ /, "", coord) if (coord != "") { print coord # 直接输出到结果,也可存入数组后续处理 } else { print "Not adding" > "/dev/stderr" } } else { print "Not adding" > "/dev/stderr" } } else { print "Not adding" > "/dev/stderr" } }
使用方式:
把上面的内容保存为process.awk,然后执行:
awk -f process.awk your_input_file.txt > output.txt
为什么这是最优解?
- Awk从头读到尾只启动一个进程,没有循环内的进程创建开销
- 正则匹配和字符串处理都是Awk原生实现,比bash内置操作效率更高
- 直接将结果输出到文件,无需在内存中存储大数组,节省内存资源
原脚本的核心问题总结
- 频繁创建子进程:每次循环调用
echo、grep、3次cut,总共5个外部进程,大文件下进程调度开销巨大 - 冗余操作:
echo -e "${line}"完全没必要,直接使用$line即可,额外的echo属于无效开销 - 低效的子串提取:多次通过管道传递数据,数据在多个进程间拷贝,浪费时间和内存
内容的提问来源于stack exchange,提问作者Sudersan
相关产品推荐
相关产品推荐

