我有一个包含以下内容的文件:第二行的“密码”为“ somethingelse”,第三行的“名称”为“ somethingdifferent”。我不能依靠它们在文件中出现的顺序,因此不能简单地将“名称”的第一次出现替换为“某物”,并将第二次出现的“名称”替换为“某物”。实际上,我实际上需要搜索周围的字符串,以确保找到并替换了正确的东西。

到目前为止,我已经尝试过以下命令来查找和替换第一个“名称”出现的位置:

<username><![CDATA[name]]></username>
<password><![CDATA[password]]></password>
<dbname><![CDATA[name]]></dbname>


,但是它不起作用,所以我认为其中某些字符可能需要转义,等等。以便能够使用正则表达式仅匹配两个“用户名”的出现并仅替换“名称”。像这样,但是用sed

sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml


,并用“ something”替换括号中的内容。

可以吗?
/>

评论

只需注意,几乎所有基于正则表达式的解决方案,除非进行了特别的设计,否则都会在输入格式发生更改时冒被破坏的风险。正则表达式对于处理XML,SGML或派生类(在我看来是这样)是一个糟糕的选择。

已批准!考虑使用XQuery例如:w3schools.com/xquery/default.asp。这是用于检索和处理XML内容的W3C标准。

#1 楼

sed -i -E "s/(<username>.+)name(.+<\/username>)/something/" file.xml


我想这就是您要寻找的。

解释:


第一部分中的括号定义第二部分中可以重用的组(实际上是字符串)

第二部分中的等是对第一部分中捕获的第i个组的引用(编号以1)

-E启用扩展的正则表达式(+和分组所需)。


评论


+1 -E选项

– slackmart
13年6月7日在22:03

它会留下一个备份文件,其名称(原始名称)+“-E”。

–显示名称
2015年11月13日下午5:32

在OSX上,我得到'sed:1:“ s /(<用户名>。+)name(。+ ...”:RE中未定义\ 1。我将这个问题的确切示例粘贴到文件中。我在这个文件上从这个答案中运行了命令,也许OSX具有不同的语法?

– deweydb
17年1月21日在5:52

sed的gnu版本支持“ -E”参数,但不是官方参数。联机帮助页中甚至没有提及。如果要使用扩展的正则表达式,则必须使用“ -r”参数。

–艾肯·克鲁格(Ikem Krueger)
17年9月19日在17:39

@deweydb根据此答案,您应该使用\(和\)而不是(和)。

– Zhang Buzz
17年11月12日13:56

#2 楼

sed -e '/username/s/CDATA\[name\]/CDATA\[something\]/' \
-e '/password/s/CDATA\[password\]/CDATA\[somethingelse\]/' \
-e '/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/' file.txt


/username/之前的s告诉sed仅适用于包含字符串“ username”的行。

评论


优雅,高效且非常适合表壳。 +1

– lgeorget
13年6月7日在22:08

#3 楼

如果对sed不是硬性要求,请改用专用工具。

如果您的文件是有效的XML(不仅是那些看起来像XML的3个标记),则可以使用XMLStarlet:

xml ed -P -O -L \
  -u '//username/text()' -v 'something' \
  -u '//password/text()' -v 'somethingelse' \
  -u '//dbname/text()' -v 'somethingdifferent' file.xml


以上内容在用正则表达式难以解决的情况下也可以使用:


可以替换标签的值而无需指定它们的当前值。
即使只是转义并且不包含在CDATA中,也可以替换它们的值。标签,如果有多个同名。
可以通过缩进来格式化已修改的XML。

上面的简要说明:


bash-4.2$ cat file.xml
<sith>
<master>
<username><![CDATA[name]]></username>
</master>
<apprentice>
<username><![CDATA[name]]></username>
<password>password</password>
<dbname foo="bar"><![CDATA[name]]></dbname>
</apprentice>
</sith>

bash-4.2$ xml ed -O -u '//apprentice/username/text()' -v 'something' -u '//password/text()' -v 'somethingelse' -u '//dbname/text()' -v 'somethingdifferent' file.xml
<sith>
  <master>
    <username><![CDATA[name]]></username>
  </master>
  <apprentice>
    <username><![CDATA[something]]></username>
    <password>somethingelse</password>
    <dbname foo="bar"><![CDATA[somethingdifferent]]></dbname>
  </apprentice>
</sith>


#4 楼

$ sed -e '1s/name/something/2' \
      -e '3s/name/somethingdifferent/2' \
      -e 's/password/somethingelse/2' sample.xml


您可以简单地使用地址作为“ s”前的数字来表示行号。替换第一场比赛。

#5 楼

您需要在\[.*^$/命令的正则表达式部分中引用s,在替换部分中引用\&/,并加上换行符。正则表达式是一个基本的正则表达式,此外,您还需要为s命令引用定界符。您必须改为引用该字符,但是更改定界符的目的通常是选择一个在替换文本或替换文本中都没有出现的分隔符。 >
您可以使用组来避免在替换文本中重复某些部分,并适应这些部分的变化。


#6 楼

要将“名称”单词替换为“某物”单词,请使用:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/something/g" file.xml


这将替换所有出现的指定单词。

到目前为止,所有内容都已输出到标准输出,您可以使用:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/something/g" file.xml > anotherfile.xml


将更改保存到另一个文件。

#7 楼

Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...

    -r, --regexp-extended
             use extended regular expressions in the script.


以便替换属性文件中的值

sed -i -r 's/MAIL\=(.+)/MAIL\=user@mymail.com/' etc/service.properties