问题描述
我想实现这一目标:
- 跳过第一次出现的匹配
- 对于所有其他事件(除了第一次)
- 删除包含该事件的整行
例如,如果我有这个文本:
<div>
<p>First text</p>
</div>
<div>
<p>Second text</p>
<p>Third text</p>
</div>
我正在匹配 <p>
我希望输出是:
<div>
<p>First text</p>
</div>
<div>
</div>
我试过 sed '0,/<p>/! /<p>/d'
,但它输出 unkNown command: `/'
。
我怎样才能达到我想要的结果? 我还是个新手,所以我的错误可能会显得很愚蠢。 如果您能提供帮助,将不胜感激。
解决方法
从这个问题来看,在我看来,您没有考虑 <p>
和 </p>
位于不同行的情况,您甚至都不关心 </p>
;您只是删除了所有包含 <p>
的行,除了第一行。
以下命令应该可以完成这项工作:
sed -z 's/<p>/\x0/;s/[^\n]*<p>[^\n]*\n//g;s/\x0/<p>/' input_file
这个解决方案有一个相当简单的逻辑:
- 它标记并“隐藏”第一个
<p>
; - 删除所有包含
<p>
的行,除了第一个<p>
是“隐藏的”; - 恢复“隐藏的”
<p>
。
详细说明:
- 选项
-z
使 Sed 将文件视为由所有行连接而成的单个字符串,每行以\n
结尾; - Sed 命令由以
;
分隔的 3 部分组成:-
s/<p>/\x0/
将第一个<p>
更改为\x0
,这不是文件中存在的字符; -
s/[^\n]*<p>[^\n]*\n//g
删除(实际上用空字符串替换)任何只包含非\n
s 且在某处带有 n<p>
且后跟\n
的行;包含<p>
的第一行不会被删除,因为它在步骤 1 之后不包含<p>
; -
s/\x0/<p>/
将标记\x0
改回<p>
。
-
当您想保留第二个 <p>
时,它与第一个在同一行,您可以使用
sed -rz ':a;s/(<p>.*\n)[^\n]*<p>[^\n]*\n/\1/;ta' file
当你真的喜欢sed
时,你可以使用
sed -n '1,/<p>/p' file; sed '/<p>/d' <(sed '1,/<p>/d' file)
你想要 sed
,我也会展示一个 awk
解决方案:
awk '/<p>/ && delp {next}
/<p>/ {delp=1}
1' file
,
这可能对你有用(GNU sed):
sed '/<p>/{x;/./{x;d};x;h}' file
如果当前行不包含 <p>
,则正常打印。
如果当前行包含 <p>
并且保留空间中有副本,则删除当前行。
否则将当前行复制到保留空间并正常打印。
替代方案:
sed -z 's/.*<p>.*\n//2mg' file
,
这是另一种使用相当复杂的逻辑但包含较短命令的解决方案:
sed 'x;s/<p>/&/;x;ta;bb;:a;/<p>/d;:b;H' input_file
这是一个描述逻辑的伪代码:
if one of previous lines contains <p>
set flag to true
else
set/leave flag to false
end
if flag
if line contains <p>
delete line
end
end
详细说明:
- 与 the other answer 不同,它不使用
-z
选项,这意味着脚本对输入的每一行都运行文件 - 脚本执行以下操作(同样,命令由
;
分隔):-
x
交换(ex
changes)模式空间的内容(其内容“通常”是正在处理的行)与保持空间的内容(一个可以存储的寄存器)最初为空的东西;请参阅第 7 步以了解我们如何在此脚本中使用它); -
s/<p>/&/
在模式空间的当前内容中搜索<p>
,即运行步骤1之前hold空间的内容,并替换为自身(&
) ;对于正在处理的文本,这是一个无操作,但它设置为 true 一个内部标志,表示上次执行的s
命令已成功;实际上,这个s
命令的作用类似于 如果模式空间包含<p>
,则将标志设置为 true,否则将其保留为 false; -
x
交换模式并再次保持空间;这些第一步(1、2 和 3)的最终效果是文本没有改变,如果 保持空间包含<p>
; -
ta
测试标志,如果为真,则控件移动到:a
所在的位置;这意味着如果保持空间包含<p>
,我们继续第 5 步,否则我们跳到第 6 步 - (紧跟在
:a
之后)/<p>/d
删除当前正在处理的行,如果它包含<p>
; - (如果第 4 步的测试结果是否定的,即保留空间不包含
<p>
,我们就在这里)bb
无条件地b
牧场(跳转)到 {{ 1}} 是,这意味着我们简单地跳过了第 5 步,即我们让包含:b
的行离开,但没有删除它; -
<p>
将当前模式空间附加到保持空间;实际上,当我们阅读它们时,我们会一行一行地累积到保持空间。
-
你和0,/<p>/! /<p>/d
很亲近! /pat/
或 /pat/!
后面不能紧跟 //
- 您需要 { }
,因此会出现语法错误。
无需重复 <p>
模式 - 空模式重复使用最后一个。
$ printf "%s\n" a '<p>' c d '<p>' '<p>' '<p>' e | sed '0,/<p>/!{//d}'
a
<p>
c
d
e