【问题标题】:Find and replace each instance of a single string with another from a list or file从列表或文件中查找单个字符串的每个实例并将其替换为另一个实例
【发布时间】:2020-07-16 07:42:45
【问题描述】:

我有一个包含多个字符串 REPLACEME 实例的大型 XML 文件。在第二个文件中,我有一个字符串列表(包含逗号),例如:

58,-21,0
234,-38,0

我想用第二个文件中的一个值替换第一个文件中REPLACEME 的每个实例,然后转到下一个实例。

我看过 bash(sed、awk、perl)和 Powershell。有人告诉我不应该使用 for 循环,而是使用文件循环并重新解析文件。所以我尝试了这个:

file2=/file2.txt
while IFS= read -r line; do
  printf '%s\n' "$line"
  sed '0,/REPLACEME/s//$line/' /file1.xml
done < "$file2"

但它什么也没做。可能是因为 XML 文件有符号?它不会出错,只是什么都不做。

如果我能找到可以实现替换结果的东西,是否愿意完全放弃我的代码或切换解析器?

编辑: 要求提供 XML 的示例。 XML 标记是 KML 文件的一部分,字符串是坐标点。下面的例子:

<Placemark>
    <name>5005</name>
        <MultiGeometry>
            <Polygon>
                -snip-
            </Polygon>
            <Point>
            <gx:drawOrder>1</gx:drawOrder>
                <coordinates>REPLACEME</coordinates>
            </Point>
        </MultiGeometry>
</Placemark>

这个文件有数百个这样的条目,需要使用file2中列表中的对应坐标来填充。

【问题讨论】:

  • 一个值是指 58,-21,058 ,-21 , 0 等等?
  • 你需要把修改后的文件放在哪里1 - 把它写出来?还是更新当前文件1?
  • 您已经展示了第二个文件的示例。您能否还显示 XML 文件的示例以及与此相对应的输出?
  • 不,单值是“58,-21,0”。我认为符号和逗号可能会造成问题,所以我将它们包括在内。 XML 示例如下所示: 5000REPLACEME 以上将是一个文件,其中包含 500 多个需要替换的条目。

标签: xml linux awk sed replace


【解决方案1】:

听起来你只需要:

awk 'NR==FNR{a[NR]=$0; next} /REPLACEME/{sub(/REPLACEME/,a[++c])} 1' file2.txt file1.xml

通常的建议是在处理 xml 文件时使用 XML 感知工具,如 xmlstarlet 或 xmllint,但我个人不太了解如何用它们解决这个问题,恕我直言,这对于你正在做的事情没有必要,假设REPLACEME 仅出现在您的示例中显示的上下文中。

在处理文本时,最好的建议不是“不要使用 for 循环”而是“不要使用 shell 循环”,因此在此使用 while 循环上下文也是一个不好的方法。见why-is-using-a-shell-loop-to-process-text-considered-bad-practice

除了操作文本(例如,从文件中读取 URL 列表以运行 curl)之外的任何其他操作时,shell 循环可能是合适的,在这种情况下(但也可以考虑 xargs)然后是的,您应该避免使用for,请参阅https://mywiki.wooledge.org/DontReadLinesWithFor

您的 sed 脚本 sed '0,/REPLACEME/s//$line/' 不会让 $line 扩展,因为它在单引号内。

【讨论】:

  • 非常感谢您为我添加有关 shell 循环和脚本问题的上下文。我感谢为教育我而付出的额外努力,而不仅仅是提供答案。我假设我需要将 awk 的输出通过管道传输回文件是正确的吗?将其通过管道传输到新文件中的最佳做法是,还是将管道传输回“file1.xml”也可以接受?我知道单独的问题,但为了完整起见......
  • 不客气。切勿将任何命令的输出重定向回输入文件,因为这样做会在命令运行之前清空输入文件。对任何给定的命令执行cmd file &gt; tmp &amp;&amp; mv tmp file
  • @EdMorton WRT 关于0,/RE/ 的第二点,与awk 不同,sed 将数字地址解析为行号。并且 GNU sed 允许 start the range on line number 0 以便 /RE/ 可以在第一行结束范围(而 1,/RE/ 不能)。
  • 啊,我明白了。范围表达式本质上是邪恶的,所以我从不使用它们。我应该这么说而不是猜测它的意思:-)!我从我的回答中删除了该声明,谢谢。
【解决方案2】:

结合使用seded - sed 从您的file2.txt 创建ed 命令:

(sed 's|.*|/REPLACEME/s/REPLACEME/&/|' file2.txt; echo '1,$p') | ed -s file1.xml

XML 文件中的第一个REPLACEMEfile2.txt 的第一行替换,第二个被第二个替换,以此类推。

如果您想保存对文件所做的更改,而不是仅仅p将它们输出到标准输出,请将echo '1,$p' 替换为echo w

如果file2.txt 中的行多于file1.xml 中的REPLACEME 行,ed 将在标准错误中为每个行打印一个问号。如果您不想看到这些,请重定向到/dev/null

$ (sed 's|.*|/REPLACEME/s/REPLACEME/&/|' file2.txt; echo '1,$p') | ed -s file1.xml 2>/dev/null
<Placemark>
    <name>5005</name>
        <MultiGeometry>
            <Polygon>
                -snip-
            </Polygon>
            <Point>
            <gx:drawOrder>1</gx:drawOrder>
                <coordinates>58,-21,0</coordinates>
            </Point>
        </MultiGeometry>
</Placemark>

如果不是2&gt;/dev/null,您的示例文件也会生成一个?,因为file2.txt 有两行并且XML 文件中只有一个REPLACEME。

【讨论】:

  • 谢谢肖恩。我很欣赏非破坏性的例子,有助于分解和测试它。该命令似乎不起作用;它只打印两行 ?特点。 Ed 的回答有效,但我不介意了解 sed 和 ed 在这种情况下在做什么?
  • @TylerJones 如果 file2.txt 中的行数多于 file1.xml 中的 REPLACEME 行数,您将在标准错误中得到问号。它们可以被忽略。如果您不想看到它们,请将标准错误重定向到 /dev/null。
猜你喜欢
  • 2014-11-16
  • 2020-12-17
  • 2017-12-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
  • 2014-09-24
相关资源
最近更新 更多