求助:awk脚本实现文本字符串提取替换时部分内容未生效
问题:修复AWK脚本的字符串替换异常
需求说明:解析文本文件,完成以下操作:
- 提取双引号包裹且不含方括号的字符串S1
- 将S1转为全大写生成常量定义S2,格式为
public static final String 大写S1 = "S1"; - 将所有S2插入文件顶部
- 把原文件中所有的
"S1"替换为对应的S2
示例输入(In.txt)
{ put("legalName", "legalName"); put("operName", "operName"); for(int i = 0; i < 3; i++) { put("firstName["+ i + "]", "firstName[" + (i+1) + "]"); put("lastName["+ i + "]", "lastName[" + (i+1) + "]"); } }
期望输出(Out.txt)
public static final String LEGALNAME = "legalName"; public static final String OPERNAME = "operName"; { put(LEGALNAME, LEGALNAME); put(OPERNAME, OPERNAME); for(int i = 0; i < 3; i++) { put("firstName["+ i + "]", "firstName[" + (i+1) + "]"); put("lastName["+ i + "]", "lastName[" + (i+1) + "]"); } }
尝试过程与问题现象
最初使用的AWK命令
awk -F'"' ' {if ($2 != "" && $2 !~ /\[/) {print "public static final String " toupper($2), "= \"" $2 "\";"}}' In.txt
修改后的tst2.awk脚本
NR == FNR { arr[NR]=$0; tailBeg = 1 while ( match(substr($0,tailBeg),/"[[:alpha:]_][[:alnum:]_]*"/) ) { strBeg = tailBeg + RSTART strLen = RLENGTH - 2 string = substr($0,strBeg,strLen) strings[string] locs[FNR] = locs[FNR] RS strBeg " " strLen tailBeg += (RSTART + RLENGTH - 1) } next } FNR == 1 { for ( string in strings ) { map[string] = toupper(string) printf "public static final String %s = \"%s\";\n", map[string], string } for (i = 1; i <= length(arr); i++) { for ( string in strings ) { map[string] = toupper(string) text = arr[i] gsub("\""string"\"", map[string], text) } printf "%s\n", text } }
执行命令
awk -f tst2.awk In.txt In.txt > Out2.txt
异常现象
实际输出中部分符合条件的"S1"未被替换,例如第一个put("legalName", "legalName");里的"legalName"未替换为LEGALNAME,输出结果如下:
public static final String LEGALNAME = "legalName"; public static final String OPERNAME = "operName"; { put("legalName", "legalName"); put(OPERNAME, OPERNAME); for(int i = 0; i < 3; i++) { put("firstName["+ i + "]", "firstName[" + (i+1) + "]"); put("lastName["+ i + "]", "lastName[" + (i+1) + "]"); } }
解决方案
问题根源:替换逻辑中,每次循环都重新赋值text = arr[i],导致只有最后一个字符串的替换生效(如operName),之前的替换结果被覆盖。
修复后的脚本(tst_fixed.awk)
NR == FNR { arr[NR] = $0; tailBeg = 1 # 匹配所有双引号包裹的内容,后续过滤含方括号的项 while (match(substr($0, tailBeg), /"[^"\[]*"/)) { strBeg = tailBeg + RSTART strLen = RLENGTH - 2 string = substr($0, strBeg, strLen) # 仅保留不含方括号的字符串 if (string !~ /\[/) { strings[string] } tailBeg += RSTART + RLENGTH - 1 } next } FNR == 1 { # 输出所有常量定义 for (string in strings) { map[string] = toupper(string) printf "public static final String %s = \"%s\";\n", map[string], string } # 逐行处理原文本,批量替换 for (i = 1; i <= length(arr); i++) { text = arr[i] # 在同一行文本上依次执行所有替换操作 for (string in strings) { gsub("\"" string "\"", map[string], text) } printf "%s\n", text } }
关键修改点
- 调整匹配正则为
/"[^"\[]*"/,确保能捕获所有双引号包裹的内容,再通过后续判断过滤掉含方括号的项,避免漏匹配 - 将
text = arr[i]移到外层循环,确保每一行仅初始化一次,所有替换操作都在同一个text变量上执行,不会覆盖之前的替换结果
执行命令
awk -f tst_fixed.awk In.txt In.txt > Out.txt
执行后即可得到符合预期的输出,所有符合条件的"S1"都会被正确替换为对应的S2常量。
内容的提问来源于stack exchange,提问作者albertkao9
相关产品推荐
相关产品推荐

