如何替换与模式匹配的行上不同数量的字符串

如何替换与模式匹配的行上不同数量的字符串

文件1

<filename1.txt> stringA string1
<filename2.txt> stringA string2
<filename2.txt> stringB string3

文件2

<filename1.txt>      words and symbols < $ stringA words and symbols 9 
<filename2.txt>      more words and symbols % @ stringA words and symbols stringB

文件2已转换

<filename1.txt>      words and symbols < $ string1 words and symbols 9
<filename2.txt>      more words and symbols % @ string2 words and symbols string3

我的方法是循环遍历 File1 的各行来设置变量。因此,对于第 1 行,变量设置为:

filenamevar = <filename1.txt>
string_old_var = stringA 
string_new_var = string1 

然后 grep 与 filenamevar 和 string_old_var 匹配的行作为 sed 的输入。这就是我需要帮助的地方(除非你认为有更好的方法)。

目前最接近的是

grep -e "$filenamevar.*$string_old_var" File2.txt | sed s/$string_old_var/$string_new_var/ >> File2Transformed.txt

除了第二行输出两次外,其他都有效:

<filename1.txt>      words and symbols < $ string1 words and symbols 9 
<filename2.txt>      more words and symbols % @ string2 words and symbols stringB
<filename2.txt>      more words and symbols % @ stringA words and symbols string3

我也尝试过

grep -e "$filenamevar.*$string_old_var" File2 | sed s/$string_old_var/$string_new_var/

sed -i s/$string_old_var/$string_new_var/ $(grep -e "$filenamevar.*$string_old_var" File2)

但都没有产生预期的结果。

这是我正在使用的循环

Numlines=$(grep "" -c File1.txt) 
for (( line=1; line<=$Numlines; line++ )) ; do 
 filenamevar=$(awk -v line=$line 'NR == line {print $1}' File1.txt) 
 string_old_var=$(awk -v line=$line 'NR == line {print $2}' File1.txt)
 string_new_var=$(awk -v line=$line 'NR == line {print $3}' File1.txt)
 # insert proper sed grep code to test here
done

答案1

如果你决定使用 shell 循环来实现这一点,你可以这样做

while read -r fname patt repl; do 
  sed -i.bak "/$fname/s/^$patt/$repl/" File2
done < File1

但请注意,如果任何字符串包含正则表达式特殊字符序列,它可能会以意想不到的方式中断。另一个选项(对特殊字符有相同的警告)可能是这样的

awk '{printf "/^%s/s/%s/%s/\n",$1,$2,$3}' File1 | sed -f- File2

它用于awk将 File1 格式化为一系列命令,然后通过管道sed -f修改 File2。

使用支持二维数组的 GNU awk (又名gawk),您可以执行以下操作:

$ gawk '
    NR==FNR {a[$1][$2] = $3; next} 
    $1 in a {for(i=2;i<=NF;i++) $i = $i in a[$1] ? a[$1][$i] : $i} 
    1
  ' File1 File2
<filename1.txt> words and symbols < $ string1 words and symbols 9
<filename2.txt> more words and symbols % @ string2 words and symbols string3

或者如果您需要保持对齐,请使用以下index函数substr

$ gawk '
    NR==FNR {a[$1][$2] = $3; next}
    $1 in a {
      for(s in a[$1]) {
        mstart = index($0,s);
        if(mstart > 0) $0 = substr($0,1,mstart-1) a[$1][s] substr($0,mstart+length(s))}
    }
    1
  ' File1 File2
<filename1.txt>      words and symbols < $ string1 words and symbols 9
<filename2.txt>      more words and symbols % @ string2 words and symbols string3

在 vanilla awk 中类似的东西,用 FS 分隔的字符串伪造二维数组:

mawk '
  NR==FNR {a[$1 FS $2] = $3; next}
  {
    for(k in a) {
      split(k,b);
      if($1 == b[1]) {
        mstart = index($0,b[2]);
        if(mstart > 0) $0 = substr($0,1,mstart-1) a[k] substr($0,mstart+length(b[2]));
      }
    }
  }
  1 
' File1 File2

相关内容