使用 sed 替换特定标签之间的子字符串

使用 sed 替换特定标签之间的子字符串

我想将两个 sed 命令合并为一个,但真的不知道该怎么做,我尝试了多种方法但没有成功。

对于结果,我希望将特定标记之间的所有斜杠替换为反斜杠,这会导致:

来源 :

<FilePath>a/b/c/d</FilePath> 
<OtherTags>Bob</OtherTags>
<FilePath>1/2/3/4</FilePath>

结果 :

<FilePath>a\b\c\d</FilePath>
<OtherTags>Bob</OtherTags>
<FilePath>1\2\3\4</FilePath>

我发现这个命令可以更改标签之间的文本:

sed -i -e 's/\(<FilePath>\).*\(<\/FilePath>\)/<FilePath>TEXT_TO_REPLACE_BY<\/FilePath>/g' test.txt

但是这个命令替换了所有文本...所以我想要的是保留文本并且只使用这种命令用反斜杠替换斜杠:

sed -e 's/\\/\//g' test.txt

但我很难将这两者结合起来。

感谢您的帮助。

答案1

使用 GNU awk 将第三个参数匹配到 match():

awk 'match($0,/(.*<FilePath>)(.*)(<\/FilePath>.*)/,a){ gsub("/","\\",a[2]); $0=a[1] a[2] a[3] } 1' file
<FilePath>a\b\c\d</FilePath>
<OtherTags>Bob/Smith</OtherTags>
<FilePath>1\2\3\4</FilePath>

以上是使用以下输入运行的:

$ cat file
<FilePath>a/b/c/d</FilePath>
<OtherTags>Bob/Smith</OtherTags>
<FilePath>1/2/3/4</FilePath>

答案2

命令

sed -e 's/\//\\/g' -e 's/<\\/<\//g' filename

输出

<FilePath>a\b\c\d</FilePath> 
<OtherTags>Bob</OtherTags>
<FilePath>1\2\3\4</FilePath>

答案3

使用扩展正则表达式模式的 GNU sed 我们可以逐步匹配在同一行上打开和关闭的 xml 标签 FilePath,并假设该标签不是引号或注释的一部分。

sed -Ee ' :a;s|<(FilePath)>([^/]*(/[^/]*)*)/([^/]*</\1>)|<\1>\2\\\4|;ta' file
perl -lpe '
 s{<FilePath>\K.*?(?=</FilePath>)}
  <$& =~ tr|/|\\|r>xge;
' file

我们隔离标签开始和结束之间的部分,并将其中的部分中的正斜杠转换为反斜杠。

我们可以编写多行正则表达式以便于表达意图。

snr='
s|
  <(FilePath)>
   ( [^/]* ([/][^/]*)* )
          /
   ( [^/]* )
  </\1>
|<\1> \2 \\ \4 </\1>|
'
ws=$'\t \n'
sed -E ":a;${snr//[$ws]/};ta" file

答案4

这是一个相当简单的解决方案,使用 sed 和 tr 通过管道构建。它假设:

  • 内部没有嵌套标签<FilePath>…</FilePath>(替换仅执行到下一个<after <FilePath>)。
  • <FilePath>不出现在文字字符串内(no<![CDATA[<FilePath>/blah]]><mytag label="<FilePath>">)。
  • 即使最后一行没有终止换行符,您的 sed 实现也会正确处理它。

原理是使用tr换行符<和换行符;这样,当 sed 处理“行”时,它实际上是在处理一个打开/关闭标记和文本打开/关闭标记之间的文本。

tr '<\n' '\n<' | sed '/^FilePath>/ y:/:\\:' | tr '<\n' '\n<'

这是一个 Perl 解决方案。它假设:

  • 里面没有嵌套标签<FilePath>…</FilePath>
  • <FilePath>不出现在文字字符串内(no<![CDATA[<FilePath>/blah]]><mytag label="<FilePath>">)。
  • <FilePath>后面总是</FilePath>在同一行)。

构造相当自然:它将函数应用于backslashify内的文本<FilePath>…</FilePath>。正则表达式.*?是非贪婪匹配:如果同一行上有多个块,则贪婪匹配.*将替换该行中从第一个<FilePath>到最后一个的所有内容。是一个更高级的版本,可以避免替换紧接在 之前或之后的斜杠,从而允许嵌套标签。</FilePath><FilePath>…</FilePath>s:(?!<<)/(?!>):\\:tr:/:\\:><

perl -pe 'sub backslashify {local $_ = $_[0]; s:(?!<<)/(?!>):\\:; return $_} s:(<FilePath>)(.*?)(</FilePath>):$1.backslashify($2).$3:e'

相关内容