使用sed根据属性内容替换XML

使用sed根据属性内容替换XML

我必须根据参数替换 XML 标记中的一些属性内容$1

我们有输入,例如:

<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true">
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="true">

如果testname属性有不是包含$1,然后将该enabled值替换为false;否则 (testname 包含$1),将该enabled值替换为true.

笔记:可能会遇到其他属性,比本例中的属性更多。

我想过,sed但也许其他工具可以做得更好?

答案1

还没人说过,所以我会的。请不要使用正则表达式解析 XML。 XML 是一种上下文语言,而正则表达式则不是。这意味着您创建了脆弱的代码,有一天可能会乱七八糟地崩溃。

更多示例请参见:https://stackoverflow.com/questions/1732348/regex-match-open-tags- except-xhtml-self-contained-tags

请使用解析器。它们存在于多种语言中 - 就我个人而言,我喜欢perl,并且您的任务有点像这样:

#!/usr/bin/env perl
use strict;
use warnings;

#parser library
use XML::Twig; 

#ingest data 
my $twig = XML::Twig -> parse (\*DATA); 

#iterate all tags <ThreadGroup>
foreach my $group  ( $twig -> get_xpath('//ThreadGroup') ) {
   #check testname regex match
   if ( $group -> att('testname') =~ /AA/ ) { 
       #set enabled
       $group -> set_att('enabled', 'true');
   }
   else {
      #set disabled
      $group -> set_att('enabled', 'false'); 
   }
}

#pretty print options vary, see man page. 
$twig -> set_pretty_print('indented_a');
$twig -> print;

__DATA__
<xml>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true" />
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="true" />
</xml>

是的 - 它必须使用 XML 解析器,因为正则表达式无法安全地完成此操作。 XML 中的许多内容在语义上是相同的,例如属性排序、换行、一元标记等,但它们的正则表达式不同。但解析器不会被它抓住。

如果您愿意,以上内容可以缩减为一行:

perl -MXML::Twig -e 'XML::Twig -> new ( twig_handlers => { ThreadGroup => sub { $_ -> set_att("enabled", $_ -> att("testname") =~ /AA/ ? "true" : "false" ) } } ) -> parsefile_inplace("yourfile")'

您的系统管理团队应该感谢您这样做(这并不是说他们会这样做),因为任何基于正则表达式的解决方案可能有一天会无缘无故地崩溃。

作为一个最简单的例子 - 您的 XML 在语义上与以下内容相同:

<xml>
  <ThreadGroup
      enabled="true"
      guiclass="ThreadGroupGui"
      testclass="ThreadGroup"
      testname="OO CSS DPM PRI"
  />
  <ThreadGroup
      enabled="true"
      guiclass="ThreadGroupGui"
      testclass="ThreadGroup"
      testname="AA CSS DPM PRI"
  />
</xml>

或者:

<xml>
  <ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGroup"
testname="OO CSS DPM PRI"/>
  <ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGroup"
testname="AA CSS DPM PRI"/>
</xml>

或者:

<xml><ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGrou
p" testname="OO CSS DPM PRI"/><ThreadGroup enabled="true" guiclass="ThreadGroupG
ui" testclass="ThreadGroup" testname="AA CSS DPM PRI"/></xml>

或者:

<xml
><ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="OO CSS DPM PRI"
/><ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="AA CSS DPM PRI"
/></xml>

那是在我们进入属性排序、可能的标签嵌套或在您意想不到的地方“匹配”的其他子字符串之前。

答案2

使用XML小星:

#!/bin/sh

xml ed -u "//ThreadGroup[. = contains(@testname, '$1')]/@enabled" -v "true"
       -u "//ThreadGroup[. = not(contains(@testname, '$1'))]/@enabled" -v "false"

假设您的 XML 是有效的(我添加了一个根标记,并用<SomeTag>正确分隔了空节点。我还将属性设置为,以便脚本实际上<ThreadGroup>/>enabled"hello"某物):

$ cat data.xml
<SomeTag>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="hello"/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="hello"/>
</SomeTag>

$ sh script.sh "OO" <data.xml
<?xml version="1.0"?>
<SomeTag>
  <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true"/>
  <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="false"/>
</SomeTag>

答案3

如果我正确理解逻辑,这个 sed 命令会在值内搜索给定的 $1 参数testname;如果存在,则搜索该enabled值并将其从 false 替换为 true。如果 ( ) 不存在!,则将该enabled值从 true 替换为 false。

sed '/ testname="[^"]*'"$1"'[^"]*"/ s/ enabled="false"/ enabled="true"/;
     / testname="[^"]*'"$1"'[^"]*"/!s/ enabled="true"/ enabled="false"/' input > output

我尝试通过在属性名称(testname 和enabled)之前添加前导空格并使用字符类来帮助正则表达式匹配[^"]

答案4

#/bin/sh

sed -r 'h
    s/.*testname="([^"]*)".*/\1/
    /\b'"$1"'\b/{ g; s/enabled="[^"]*"/enabled="true"/; b}
    g
    s/enabled="[^"]*"/enabled="false"/
' file

相关内容