Bash:将匹配的正则表达式替换为另一个文件的第n行

时间:2016-02-14 19:53:08

标签: regex bash sed

我有两个文件。

文件一是TXT并包含字符串,每行一个。

文件二是一个包含多个条目的XML:

<data name="Btn:Cancel" xml:space="preserve">
    <value>Cancel</value>
    <comment>Original English: Cancel</comment>
  </data>

我需要将XML文件中的VALUE值替换为来自文件1的相应字符串:因此第一次出现的VALUE将被替换为文件1中的第一行,第二次出现的是XML文件中的VALUE被文件一中的第二行替换,依此类推。

我尝试了几件事(基本上是使用sed)而我的最后一次拍摄是

while read line           
do
    echo $count
    echo $line
    sed "s_<value>.*</value>_<value>$line</value>_$count" file.xml > results.xml
    ((count++))
done < file.txt

但它对results.xml文件没有任何作用:(

6 个答案:

答案 0 :(得分:1)

这可能适合你(GNU sed):

sed -n '/<value>/=' file.xml |
sed 'R file.txt' | 
sed 'N;s/\(.*\)\n\(.*\)/\1s#<value>[^<]*#<value>\2#/' |
sed -f - file.xml > file1.xml

此解决方案:查找xml文件中每个<value>行的行号。然后将txt文件中的值附加到每个行号。将这两者组合成一个sed指令,其中包含每个<value>的地址和值。然后将生成的sed命令应用于xml文件以生成结果。

答案 1 :(得分:1)

像这样修改你的剧本

count=1
# test.xml is your file
cat test.xml | tr "\n" "\t" > test2.xml
while read line
do
    echo $count
    echo $line
    sed -i " s_<value>[^<]*</value>_<value>$line</value>_${count}; " test2.xml
    ((count++))
done < file.txt
cat test2.xml | tr "\t" "\n" >test3.xml
  • 初始化计数
  • 不同的正则表达式([^>]*
  • 使用tr将test.xml转换为一个长行,sed s命令可以使用number flag
  • 使用另一个tr来转换长行

答案 2 :(得分:1)

使用awk的解决方案:

awk '/<value>.*<\/value>/{getline newval<"file.txt";sub(/[^>]*<\/value>/,newval"</value>")}1' file.xml

或者,更详细一点:

#!/usr/bin/awk -f

# If we match the <value></value> line
/<value>.*<\/value>/ {

    # Read next line from txt file
    getline newval < "file.txt"

    # Substitute value between tags
    sub(/[^>]*<\/value>/, newval "</value>")
}

# For all lines: print
{ print }

作为一个好公民,我会提到使用专用XML解析器以外的工具解析XML通常不是一个好主意。命令行XML解析可以使用例如

来完成

答案 3 :(得分:0)

您可能希望使用sed命令的--in-place(或-i)参数,如下所示:

sed -i 's/hello/test/' your_file

这将修改文件。

希望这有帮助!

答案 4 :(得分:0)

我建议使用真正的编程语言,例如Perl。例如:

perl -e ' use warnings;
          use strict;

          open my $new_values_fh, "<", "file.txt" or die;

          while (<>) {
              if (m{<value>}) {
                  my $new_value = <$new_values_fh>; chomp $new_value;
                  s{(<value>).*?(</value>)}{$1$new_value$2};
              }
              print;
          }
        ' < file.xml > results.xml

答案 5 :(得分:0)

听起来你只需要:

awk 'NR==FNR{nums[NR]=$0;next} sub(/<value>.*<\/value>/,"<value>"nums[i+1]"</value>"){i++}' file.txt file.xml

但是很难猜测你问题中的小样本XML输入文件并没有关联的TXT文件,所以我们没有什么可以测试的。

请记住这一点:

  1. 每次在shell中编写一个循环来操作文本时,你的方法都是错误的。见https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice
  2. shell是一个操作文件和进程以及对工具进行调用的环境。用于操作文本的UNIX工具是awk。阅读由Arnold Robbins撰写的Effective Awk Programming,4th Edition一书。