使用 sed 查找和替换复杂字符串(最好使用正则表达式)

使用 sed 查找和替换复杂字符串(最好使用正则表达式)

我有一个包含以下内容的文件:

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

我需要制作一个脚本,将第一行中的“名称”更改为“something”,第二行中的“密码”更改为“somethingelse”,第三行中的“名称”更改为“something different”。我不能依赖文件中出现的顺序,因此我不能简单地将第一次出现的“名称”替换为“某事”,将第二次出现的“名称”替换为“某事不同”。实际上,我需要搜索周围的字符串,以确保找到并替换正确的内容。

到目前为止,我已尝试使用此命令来查找并替换第一个“名称”出现:

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

但是它不起作用,所以我想其中一些字符可能需要转义等。

理想情况下,我希望能够使用正则表达式来匹配两个“用户名”出现并仅替换“名称”。像这样的东西,但有sed

<username>.+?(name).+?</username>

并将括号中的内容替换为“某事”。

这可能吗?

答案1

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

我想这就是您所寻找的。

解释:

  • 第一部分中的括号定义了可以在第二部分中重用的组(实际上是字符串)
  • \1第二部分中的、\2等是对第一部分中捕获的第 i 个组的引用(编号从 1 开始)
  • -E+启用扩展正则表达式(分组所需)。
  • -i启用“就地”文件编辑模式

答案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/的 告诉ssed 仅对包含字符串“username”的行起作用。

答案3

如果sed不是硬性要求,最好使用专用工具。

如果您的文件是有效的 XML(不仅仅是那 3 个看起来像 XML 的标签),那么您可以使用XML小星:

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

您需要\[.*^$/在命令的正则表达式部分s\&/替换部分中引用,并加上换行符。正则表达式是一个基本正则表达式,此外您还需要引用s命令的分隔符。

您可以选择不同的分隔符以避免引用/。您必须改为引用该字符,但通常更改分隔符的目的是选择要替换的文本或替换文本中未出现的分隔符。

sed -e 's~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~'

您可以使用组来避免重复替换文本中的某些部分,并适应这些部分的变化。

sed -e 's~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~'

sed -e 's~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~'

相关内容