<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test time="60" id="01">
<java.lang.String value="cat"/><java.lang.String value="dog"/>
<java.lang.String value="mouse"/>
<java.lang.String value="cow"/>
</test>

我想做的是,我想编辑文件,以便当我得到像<java.lang.String value="something"/>这样的东西时,我将把该部分改为<animal>something</animal>

因此,对于前面的例子,在应用了sed/awk/grep命令的脚本后,文件内容将被改变为或创建一个新的文件,如下所示。

   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <test time="60" id="01">
    <animal>cat</animal><animal>dog</animal>
    <animal>mouse</animal>
    <animal>cow</animal>
    </test>

我试着用下面的命令来提取那个特定的部分。

$less test.txt | grep -Po 'java.lang.String value="\K[^"]*' | awk -F: '{print "<animal>" $1 "</animal>"}'

输出给我的是改变过的部分,但我希望这个改变过的部分和文件的其他部分都不改变。

<animal>cat</animal>
<animal>dog</animal>
<animal>mouse</animal>
<animal>cow</animal>

我是脚本新手,我不知道如何将完整的输出写入文件。

1 个评论
zmo
请尽量使用正确的工具来完成正确的任务。对XML文档的转换应使用XSL转换,而不是sed/grep/等等。有标准的工具,它可以检查你的输入文件,因为它给你的是正确的结果。
regex
linux
bash
sed
grep
web2dev
web2dev
发布于 2014-05-07
2 个回答
Grapsus
Grapsus
发布于 2014-05-07
已采纳
0 人赞同
sed -r 's#<java.lang.String value="([^"]*)"/>#<animal>\1</animal>#g' test.txt

而且你不应该用正则表达式做XML的转换...

关于它如何工作的编辑

默认情况下,sed使用 "基本正则表达式",其中许多特殊字符必须以\为前缀。-r标志切换到 "扩展正则表达式",其中的语法不那么繁琐。参见OpenGroup详情请见下文。

默认情况下,sed按原样打印输出,除非命令修改它。替换命令就像s#search_regexp#replacement#flags。分隔符可以是任何东西,如/#,。我选择#,这样它就不会与XML中的\字符相冲突。

然后,我们匹配像<java.lang.String value="anything_except_quotes"/>这样的东西。我们想要重用的部分有括号,它被称为匹配组。在替换中,我们用\1来指代我们在匹配组里面捕获的东西。

g标志使sed替换所有搜索模式的出现,而不仅仅是第一个出现。

谢谢,你能不能解释一下,这怎么会有正确的输出?我不善于使用sed
zmo
zmo
发布于 2014-05-07
0 人赞同

你的命令有一些问题。

less test.txt | grep -Po 'java.lang.String value="\K[^"]*' | awk -F: '{print "<animal>" $1 "</animal>"}'

首先,有一个无用的less的使用,grep可以把一个文件作为一个参数。

grep -Po 'java.lang.String value="\K[^"]*' test.txt | awk -F: '{print "<animal>" $1 "</animal>"}'

那么你就用grep来选择匹配字符串的行,所以基本上,你的命令序列是explicitely只保留有java.lang...字符串的行,把其他的都去掉。一个更简单的解决方案是使用sed

sed -r 's,<java.lang.String value="([^"]*)"\s*/>,<animal>\1</animal>,g' test.txt

其中使用sed的替换语法来替换匹配,同时提取括号中的()作为\1的右边部分。[^"]部分是为了匹配所有不是"的字符,而*运算符是为了应用0次或多次的匹配。替换代码13】是为了匹配一个空格,*,0次或更多次。

词条是一种自动机,它使用状态和转换来匹配给定的字符串。下面是一个关于重组词如何工作的视觉效果。

在一个例子上演示的重码法

虽然在你的特殊情况下,这个简单的重组词是有效的,但请记住,这只是一个黑客.你应该使用一个XML解析器并替换节点以满足你的需要,使用XSLT/XSLFO,这些工具被设计用来将一个XML转化为另一个(或其他)。

要做到这一点,你可以使用一个工具,如xsltproc,看看this Q对于一个将XML树中的所有foo节点转换为bar节点的例子,这里是如何做的。

test.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <!--Identity Template. This will copy everything as-is.-->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  <!--Change "java.lang.String" element to "animal" element.-->
  <xsl:template match="java.lang.String">
    <animal>
      <!-- get the attribute 'value' of java.lang.String -->
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates/>
    </animal>
  </xsl:template>
</xsl:stylesheet>
xsltproc test.xsl test.xml

result:

<?xml version="1.0"?>
<test time="60" id="01">
  <animal value="cat"/>
  <animal value="dog"/>
  <animal value="mouse"/>