“sed”正则表达式帮助:替换字符

“sed”正则表达式帮助:替换字符

我想使用 sed 更改 XML 文件中的字符。输入如下:

<!-- Input -->
<root>
  <tree foo="abcd" bar="abccdcd" />
  <dontTouch foo="asd" bar="abc" />
</root>

现在我想将树元素的bar标签中的所有c都改为X。

<!-- Output -->
<root>
  <tree foo="abcd" bar="abXXdXd" />
  <dontTouch foo="asd" bar="abc" />
</root>

正确的 sed 命令是怎么样的?请考虑一下,一个标签中可以出现多个 c(彼此相邻或不相邻)...

我自己尝试过这个,但它不会改变多个 c,并且它确实附加了一个 X :(

sed -i 's/\(<tree.*bar=\".*\)c\(.*\"\/>\)/\1X\2/g' Input.xml

编辑:更多细节;)

  • 这是一生一次的工作,文档更改后,我不会再碰它

  • 结构与上面一样简单。这意味着,我可以使用以下命令抓取所有行(这有效):

    cat 输入.xml | grep ""

因此,假设我已经提取了正确的字符串,并且知道修改后将其写入何处:如何将“abcdeccd”更改为“abXdeXXd”?这实际上不是 XML 问题,而是正则表达式问题,还是我错了?

答案1

这可能对你有用(GNU sed?):

sed '/^\s*<tree.*\<bar="/!b;s//&\n/;:a;s/\n\([^c"]\+\)/\1\n/;ta;s/\nc/X\n/;ta;:b;s/\n//' XML

答案2

正如 RedGrittyBrick 所说,最好的实现此目的的方法是使用 XML 解析器,挑选出元素,翻译字符,然后使用 XML 库将其写回。这不会给您带来令人不快的意外,它将经受住时间的考验,等等。它不仅是最好的,而且远远优于其他东西。其他解决方案或多或少会立即成为调试的噩梦,而且肯定会到处都或多或少隐藏着问题。

如果只是一个简单的任务,只需要做一次,而且一个人非常小心,并且检查结果等等,那么用坏方法做可能会省力。但将要如果你养成习惯,有一天你会感到惊喜。

举个例子,这是一个似乎有效的糟糕方法,它不仅依赖于有效的XML,而是你之前描述的或多或少精确的语法,这只是一个子集有效的 XML,因此有效的 XML 肯定会使代码失败(如果有人在其中一个标签中添加了“>”符号怎么办?添加一个特殊情况。如果有人不使用引号怎么办?添加一个特殊情况,等等)。这是不使用真实解析器的问题。下面至少采取了一些措施来像伪解析器一样工作,读取标签,然后对其采取行动,然后将其写回,但已经存在经过广泛测试的现成工具。

#!/bin/sh
IFS='\n'
while read i; do
    if $(printf -- "${i}" | grep -qE '<tree [^>]+ bar="[^'"${1}"'"]*'"${1}"); then
        ORIGTAG=$(printf -- "${i}" | sed 's#^.*<tree [^>]\+ bar="\([^"]\+\)".*$#\1#g')
        NEWTAG=$(printf -- "${ORIGTAG}" | tr "${1}" "${2}")
        printf -- "${i}\n" | sed 's#\(^.*<tree [^>]\+ bar="\)'"${ORIGTAG}"'\(".*$\)#\1'"${NEWTAG}"'\2#g'
    else
        printf -- "${i}\n"
    fi
done < "${3}"

用法:script.sh [要替换的字符] [替换字符] [文件名],例如

script.sh c X myfile

IFS将 shell 中的“内部字段分隔符”设置为换行符,以保留行首的空格。

while read逐行读取输入文件(作为脚本的第 3 个参数传入)。

grep检查特定标签是否在当前行中,以及标签是否包含要翻译的字符。如果是,则转到sed逻辑;如果不是,则按原样返回该行。

sed选出旧标签,对其进行字符翻译并返回带有新标签的行。

正如您所看到的,没有人愿意找到这个脚本并对其进行调试。如果这是任何事物除了一次性工作外,不要这样做。为了未来观察者的理智。

相关内容