【问题标题】:Bash: replace matching regex with nth line from another fileBash:用另一个文件中的第 n 行替换匹配的正则表达式
【发布时间】:2016-02-15 09:18:08
【问题描述】:

我有两个文件。

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

文件 2 是一个 XML,其中包含多个条目,如下所示:

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

我需要用文件一中的相应字符串替换 XML 文件中的 VALUE 值:因此,第一次出现的 VALUE 将被文件一中的第一行替换,第二次出现在 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 文件没有任何作用:(

【问题讨论】:

  • 在您的脚本中将计数初始化为 1,但我认为这不会起作用,因为 sed 逐行工作。每行最多有一个值。您必须首先将整个 xml 压缩成一行。你能用一行xml试试吗?并将.* 更改为.*?。也可以按照 napnac 的建议使用 -i
  • 我试过这个:tr '\n' '^' temp.txt 但也没有乐趣......
  • @EdMorton 确实,两者看起来都一样,我还没有看到这个。但是,我知道另一个人想要一遍又一遍地替换相同的 XML,而我有一个 XML 文件,其中包含很多我想要替换的数据。如果我错了,请纠正我。
  • 你错了。您的问题与另一个具有完全相同解决方案的问题完全相同 - 将 TXT 文件读入数组,然后使用递增索引将 XML 文件中的每个目标字符串替换为数组内容。我在这里发布了我对该问题的回答,并根据您的具体情况进行了调整。

标签: regex bash sed


【解决方案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
  • 初始化计数
  • 不同的正则表达式 ([^&gt;]*)
  • 使用 tr 将 test.xml 转换为单个长行,带有 number flagsed s 命令可以处理该行
  • 用另一个tr把长线变回来

【讨论】:

  • 这是我最终使用的,还有一些其他更改,例如从任意条目开始。谢谢!
【解决方案2】:

这可能对你有用(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 文件中查找每个 &lt;value&gt; 行的行号。然后将 txt 文件中的值附加到每个行号。将两者组合成一条 sed 指令,其中包含每个 &lt;value&gt; 的地址和值。然后将生成的 sed 命令应用于 xml 文件以生成结果。

【讨论】:

    【解决方案3】:

    使用 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 解析可以使用,例如,

    【讨论】:

    • 感谢您的提示。对于这项工作,XML 非常一致,我对使用这种方法很有信心(我有 1 个异常需要处理超过 4k 的值)。
    【解决方案4】:

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

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

    这将修改文件。

    希望这会有所帮助!

    【讨论】:

    【解决方案5】:

    我建议使用真正的编程语言,例如 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
    

    【讨论】:

      【解决方案6】:

      听起来你只需要:

      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》一书。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-14
        • 2010-09-07
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-17
        • 1970-01-01
        相关资源
        最近更新 更多