我需要修复错误并将第二个标记替换</time>
为</tags>
具有以下结构的 XML 文件中的第二个标记:
<time>20260664</time>
<tags>substancesummit ss</time>
<geo>asdsadsa</geo>
<time>20260664</time>
<tags>substancesummit ss</time>
<geo>asdsadsa</geo>
我正在尝试使用 sed 来完成此操作,因为每个项目有 2 个</time>
结束标记,所以我的想法是替换</time><geo>
为</tags><geo>
.
然而,中间有一个下一行符号,所以我正在使用,\n
但它不起作用:
sed 's/time>\n<geo>/tags>\n<geo>/g' old.xml > new.xml
答案1
Sed 逐行处理输入,因此换行符永远不会自发地出现在输入中。您可以做的就是暂停以结尾的线路</time
;那么如果下一行以 开头<geo>
,则在上一行中进行替换。 (这在 sed 中是可能的,使用“保留空间”,但我建议当您需要保留空间时转向 awk 或 perl。)
但是,根据您的示例输入,您可以将其更改</time>
为</tags>
以 开头的行<tags>
。
sed -e '/^<tags>/ s!</time>$!</tags>!'
答案2
虽然也许可以通过其他方式轻松解决您的问题,但您问题的答案很简单。sed
默认情况下,在 2 个缓冲区上一次处理一行 - 一个跨行周期持续存在,称为h
旧空间,另一个每个周期至少刷新一次,称为模式空间 - 后者是执行所有编辑的地方。
可以通过以下两种方式之一获得前瞻功能 - 您可以保存旧行并落后于行周期,以便更好地利用命令来交换和比较缓冲区。这涉及命令原语,例如[hH]
old、[gG]
et、e x
change - 分别保存到、复制和换出保持缓冲区 - 小写形式覆盖,大写形式附加到其目标缓冲区。
或者,您可以将未来的行放入恒定编辑算法中,在该算法中,您可以在每个周期中始终删除与读取的输入行一样多的输入行。后者将是我在这里的偏好 - 特别是因为sed
它变得非常简单和高效 - 特别是使用N;P;D
命令。
这是使用您的示例数据的演示:
sed '$!N;s/ime\(>\n<geo\)/ags\1/;P;D
' <<\IN
<time>20260664</time>
<tags>substancesummit ss</time>
<geo>asdsadsa</geo>
<time>20260664</time>
<tags>substancesummit ss</time>
<geo>asdsadsa</geo>
IN
N
ext、P
rint 和D
elete 与它们的小写对应项一样,n;p;d
分别获取下一行输入、打印和从模式空间删除。与小写字母不同N
(如果与 的情况稍有不同),这三个工作在换行边界上,而不是作为一个整体的模式空间。
N
将把下一个输入行追加到模式空间中的\n
ewline 字符之后。P
将仅打印\n
模式空间中第一个出现的 ewline 字符。D
\n
在退出当前循环的脚本之前,将仅删除模式空间中第一个出现的ewline 并将其与模式空间中剩余的内容一起排队,或者,如果在其删除操作之后没有任何内容,则下一行等待照常输入。
这三者可以一起工作,非常简单有效地扩展sed
文件的编辑窗口 -sed
滑动文件,每个周期仅打印它根据脚本编写者的指示不断删除和补充的一系列行中最旧的行 - 这将 der 留sed
在线路周期的电荷。
并且下一行的前瞻很容易扩展。如果您希望整个脚本有一个 4 行模式空间窗口,您可以这样做:
sed -e '1{N;N' -e '};N;...;P;D'
...或者,也许更有用...
sed -e ':next
$!{/\(.*\n\)\{3\}/!{
N;b next' -e '}
};...cmds...;P;D'
...其中sed
仅绘制输入行 - 并继续这样做,直到在执行任何其他命令之前有足够的量 - 如果模式空间中的 ewline 字符少于三个\n
并且当前行不是最后一行。无论后续命令进行的编辑如何,都会发生这种情况。
答案3
文学性地回答这个问题:
我通过一个小作弊解决了这个问题(要编辑的文本跨越多行):
cat input.txt | tr '\n' '@' | sed -e 's/txt@iam@interestedin/iaminterested@intxt/g' | tr '@' '\n' > output.txt
您唯一需要确定的是您的输入中尚不存在替换换行符的字符。