如何在文件中替换指定第N个独立行的目标字符串
需求与问题
我有一个数千行的文件,存在大量重复字符串。示例内容:
123 Fragment-cnum0001 Energy Fragment-cnum0001 XXX sss 123 Fragment-cnum0001 Energy Fragment-cnum0001 XXX sss 123 Fragment-cnum0001 Energy Fragment-cnum0001 XXX sss 123 Fragment-cnum0001 Energy Fragment-cnum0001 XXX sss 123 Fragment-cnum0001 Energy Fragment-cnum0001 XXX sss
需要实现:将独立行的Fragment-cnum0001依次替换为递增编号的alpha-cnum0001、alpha-cnum0002...,替换后效果:
123 Fragment-cnum0001 Energy alpha-cnum0001 XXX sss 123 Fragment-cnum0001 Energy alpha-cnum0002 XXX sss 123 Fragment-cnum0001 Energy alpha-cnum0003 XXX sss 123 Fragment-cnum0001 Energy alpha-cnum0004 XXX sss 123 Fragment-cnum0001 Energy alpha-cnum0005 XXX sss
注意:
- 不能用行号计数,目标字符串间隔行数不固定;
- 仅替换独立行的目标,排除嵌入在其他字符串中的匹配项。
我尝试用sed结合zsh循环实现,但执行x=2;sed "s/0001/0002/$x" file无效果,原脚本:
for k in *.txt; do x=0 #reset the number of occurrences to zero tconf=$(grep -c "cnum0001\n" $k) #find the total number of occurrences while $(( (($x + 0)) <= (($tconf + 0)) )); do #while the number of occurrences is less than the total number x=$(($x + 1)) #add one to the number of occurrences cnc=$(( printf %04d $x )) #set it so that $cnc includes the necessary number of leading zeroes before $x, so if x=1, cnc=0001. cn=${prefixCFGi}-cnum${cnc} sed -i 's/Fragment-cnum0001/$cn/$x' $k #This is the command I need help with. I want it to find the xth occurrence of Fragment-cnum0001 and replace it with $cn done #loop through the txt file until $x=$tconf done #loop through all txt files
解决方案
方法1:使用awk(高效简洁)
awk可以一次遍历文件完成计数和替换,无需多次调用外部命令,适合大文件场景:
BEGIN { count=0 } /^Fragment-cnum0001$/ { count++ printf "alpha-cnum%04d\n", count next } 1
执行方式:
# 直接命令行执行,输出到新文件 awk 'BEGIN{count=0} /^Fragment-cnum0001$/{count++; printf "alpha-cnum%04d\n", count; next} 1' input.txt > output.txt # 批量处理所有txt文件并原地修改 for k in *.txt; do awk 'BEGIN{count=0} /^Fragment-cnum0001$/{count++; printf "alpha-cnum%04d\n", count; next} 1' "$k" > tmp && mv tmp "$k" done
解释:
/^Fragment-cnum0001$/精确匹配整行就是目标字符串的行,避免替换嵌入在其他内容中的匹配项;- 每匹配一次计数器
count递增,用%04d格式化出4位带前导零的编号; - 最后的
1表示打印所有未被特殊处理的行,即不匹配目标的行直接输出。
方法2:修复sed+zsh方案
原脚本存在几个语法和逻辑错误,修复后如下:
prefixCFGi="alpha" for k in *.txt; do x=0 # 正确统计独立行的目标字符串数量 tconf=$(grep -c "^Fragment-cnum0001$" "$k") while (( x < tconf )); do x=$((x + 1)) # 正确格式化带前导零的编号 cnc=$(printf "%04d" "$x") cn="${prefixCFGi}-cnum${cnc}" # 正确使用sed替换第x次匹配的独立行目标 sed -i "${x}s/^Fragment-cnum0001$/$cn/" "$k" done done
注意:这种方式需要多次调用sed修改文件,大文件场景下效率较低,仅推荐用于小文件处理。
内容的提问来源于stack exchange,提问作者Pyrodancer123
相关产品推荐
相关产品推荐

