Xml bash脚本:如何用另一行替换这些与模式匹配的行?

Xml bash脚本:如何用另一行替换这些与模式匹配的行?,xml,bash,replace,sed,Xml,Bash,Replace,Sed,我需要在bash脚本中使用sed或类似的实用程序来处理当前目录中的一组XML文件 在具有以下任一行的每个文件中(一个文件中可能有0行或1行) 使用正确解析XML的工具,而不是使用sed。例如,在中,您可以使用 for $file in { glob '*.xml' } { open $file ; for //MetaDatum/@value set . xsh:subst(., 'VALUE \(.*', 'VALUE') ; save :b ; }

我需要在
bash
脚本中使用
sed
或类似的实用程序来处理当前目录中的一组XML文件

在具有以下任一行的每个文件中(一个文件中可能有0行或1行)


使用正确解析XML的工具,而不是使用sed。例如,在中,您可以使用

for $file in { glob '*.xml' } {
    open $file ;
    for //MetaDatum/@value
        set . xsh:subst(., 'VALUE \(.*', 'VALUE') ;
    save :b ;
}

使用正确解析XML的工具,而不是使用sed。例如,在中,您可以使用

for $file in { glob '*.xml' } {
    open $file ;
    for //MetaDatum/@value
        set . xsh:subst(., 'VALUE \(.*', 'VALUE') ;
    save :b ;
}

假设
grep
命令中的模式标识了所有需要修改的行,而没有其他行,则可以编写一个与相同行匹配的
sed
命令,并在其上替换
value
属性的值:

sed '/MetaDatum key="Pr" value="VALUE/ s/value="[^"]*"/value="VALUE"/' $f
但是请注意,这种方法(grep和sed)对XML的确切细节非常敏感。它将使用不同数量的空格,特别是嵌入的换行符,额外的属性,不同的引号选择等等

其中一些问题可以通过更智能的模式解决,但其他问题则无法解决。要正确处理XML,您需要真正的XML工具。在这种情况下,合适的工具是XSLT转换。这里有一个转换可以完成这项工作(前提是源文件没有覆盖默认的XML名称空间——谢谢,CharlesDuffy):

正如@CharlesDuffy在评论中所观察到的,这可能导致名为
$f
的文件拥有不同的所有权和/或比以前更严格的权限。如何解决该问题取决于可用的工具。例如,虽然标准的
chown
chmod
没有它,但GNU版本有机制设置文件的所有权和权限,以匹配不同文件的所有权和权限。此外,您还需要考虑当“代码> $f<代码>命名一个符号链接(替换链接,或者修改它指向的文件)时所需的行为。由于这些都是环境和优先级相关的问题,如果上面给出的命令没有按照您喜欢的方式处理它们,那么您需要决定如何修改方法


如果您需要处理一个被覆盖的默认XML名称空间,那么模板需要稍微复杂一点。您需要为
MetaDatum
元素及其属性的名称空间声明名称空间前缀,并在引用这些名称时使用它。

假设
grep
命令中的模式标识所有需要修改的行,而不标识其他行,您可以编写一个与相同行匹配的
sed
命令,并替换其上
value
属性的值:

sed '/MetaDatum key="Pr" value="VALUE/ s/value="[^"]*"/value="VALUE"/' $f
但是请注意,这种方法(grep和sed)对XML的确切细节非常敏感。它将使用不同数量的空格,特别是嵌入的换行符,额外的属性,不同的引号选择等等

其中一些问题可以通过更智能的模式解决,但其他问题则无法解决。要正确处理XML,您需要真正的XML工具。在这种情况下,合适的工具是XSLT转换。这里有一个转换可以完成这项工作(前提是源文件没有覆盖默认的XML名称空间——谢谢,CharlesDuffy):

正如@CharlesDuffy在评论中所观察到的,这可能导致名为
$f
的文件拥有不同的所有权和/或比以前更严格的权限。如何解决该问题取决于可用的工具。例如,虽然标准的
chown
chmod
没有它,但GNU版本有机制设置文件的所有权和权限,以匹配不同文件的所有权和权限。此外,您还需要考虑当“代码> $f<代码>命名一个符号链接(替换链接,或者修改它指向的文件)时所需的行为。由于这些都是环境和优先级相关的问题,如果上面给出的命令没有按照您喜欢的方式处理它们,那么您需要决定如何修改方法


如果您需要处理一个被覆盖的默认XML名称空间,那么模板需要稍微复杂一点。您需要为
MetaDatum
元素及其属性的名称空间声明名称空间前缀,并在引用这些名称时使用它。

使用此sed one-liner命令修改所有xml文件(在当前目录中):

sed -i 's,\(<MetaDatum\s*key="Pr"\s*value="VALUE\).*\s*/>,\1" />,' *.xml
将命令与
find
工具结合使用,将sed应用于扩展名为.xml(不区分大小写)的文件,这些文件可以在目标目录或其子目录中找到:

find ${targetDir} -type f -iname "*.xml" -exec sed -i 's,\(<MetaDatum\s*key="Pr"\s*value="VALUE\).*\s*/>,\1" />,' {} \;
find${targetDir}-type f-iname“*.xml”-exec sed-i's,\(,'{}\;

使用此sed one-liner命令就地修改所有xml文件(在当前目录中):

sed -i 's,\(<MetaDatum\s*key="Pr"\s*value="VALUE\).*\s*/>,\1" />,' *.xml
将命令与
find
工具结合使用,将sed应用于扩展名为.xml(不区分大小写)的文件,这些文件可以在目标目录或其子目录中找到:

find ${targetDir} -type f -iname "*.xml" -exec sed -i 's,\(<MetaDatum\s*key="Pr"\s*value="VALUE\).*\s*/>,\1" />,' {} \;
find${targetDir}-type f-iname“*.xml”-exec sed-i's,\(,'{}\;

您无法可靠地将
sed
用于此作业:XML可以以太多不同的方式写入(例如,您的文档可能会将key和value属性与它们应用的值放在不同的行上,或者可能会将“value”放在“key”之前),或者可以开始使用命名名称空间,从而在内容上添加前缀。foo:)。无法保证将来生成的输入文件的格式会完全相同,尤其是在生成该文件的代码发生更改时

相反,请使用XML感知工具,例如:


顺便说一句,如果您希望就地执行编辑,
xmlstarlet-edsed -i.bak 's,\(<MetaDatum\s*key="Pr"\s*value="VALUE\).*\s*/>,\1" />,' *.xml
find ${targetDir} -type f -iname "*.xml" -exec sed -i 's,\(<MetaDatum\s*key="Pr"\s*value="VALUE\).*\s*/>,\1" />,' {} \;
xmlstarlet ed \
  -u '//MetaDatum[@key="Pr"]/@value' \
  -v "VALUE" \
  <in.xml >out.xml
xmlstarlet ed \
  -N "foo=http://example.com/foo"
  -u '//foo:MetaDatum[@key="Pr"]/@value' \
  -v "VALUE" \
  <in.xml >out.xml