如何高效有序合并文件并替换指定行?求Shell方案提速
批量合并XYZ文件并替换标题行的提速方案
需求
按顺序合并命名为FILENAME.xxx.xyz(xxx从001开始连续递增)的文件到$COMBINED_FILE,并用$ACTFILE中的能量值替换合并文件里对应构象的标题行。原for循环方案在文件数量多时速度过慢,需要优化。
原方案的性能问题
- 合并步骤:循环中每次
cat >>都会重复打开、写入、关闭$COMBINED_FILE,频繁IO拖慢速度;逐个执行mv也会增加不必要的系统调用。 - 替换步骤:循环里每次调用
awk读取$ACTFILE、调用sed -i修改合并文件——sed -i每次都会重写整个文件,当文件大、循环次数多的时候,IO开销会指数级上升。
优化实现
1. 高效合并与移动文件
一次性处理所有目标文件,减少IO操作;批量移动文件降低系统调用次数:
# 收集所有连续编号的目标文件 target_files=() current_num=1 while true; do # 格式化三位数编号 printf -v num "%03d" "$current_num" file="${FILENAME}.${num}.xyz" [[ -f "$file" ]] || break target_files+=("$file") ((current_num++)) done # 一次性合并所有文件到目标文件(仅一次写IO) cat "${target_files[@]}" > "$COMBINED_FILE" # 批量移动文件 for file in "${target_files[@]}"; do # 提取文件名中的xxx部分 num=$(echo "$file" | awk -F. '{print $(NF-1)}') mv -f "$file" "${XYZDIR}/${JOB_BASENAME}_${num}.xyz" done
2. 一次性替换所有标题行
用awk一次性完成能量值读取和替换,避免多次重写文件:
# awk脚本:先读ACT文件存能量,再处理合并文件替换标题行 awk -v lines_per_conf="$LINES_PER_CONF" ' # 读取ACT文件,将每行能量存入数组,索引为构象序号 NR == FNR { energy[FNR] = $2 next } # 处理合并后的XYZ文件 { # 判断当前行是否为标题行:第2、2+lines_per_conf、2+2*lines_per_conf...行 if ((NR - 2) % lines_per_conf == 0) { conf_index = (NR - 2)/lines_per_conf + 1 # 如果有对应的能量值,替换输出 if (conf_index in energy) { print energy[conf_index] next } } # 非标题行直接输出 print $0 } ' "$ACTFILE" "$COMBINED_FILE" > "$COMBINED_FILE.tmp" && mv "$COMBINED_FILE.tmp" "$COMBINED_FILE"
优化亮点
- 合并步骤:将多次写IO操作缩减为1次,批量移动减少系统调用,文件数量越多,提速效果越明显。
- 替换步骤:仅需读取
$ACTFILE和$COMBINED_FILE各1次,写入1次,彻底消除循环中重复读写文件的巨大开销,性能提升显著。
内容的提问来源于stack exchange,提问作者Cdc95
相关产品推荐
相关产品推荐

