使用 sed 删除两个单词之间的数据
remove data between two word with sed
假设我们有一个包含以下内容的文件:
<tag1>
junk1
junk2
</tag1>
data1
data2
data3
<tag1>junk3</tag1>
data4
data5
所以,我们要删除两个字符串之间的所有数据,即 <tag1>
和 </tag1>
。我可以使用 sed
命令完成这项工作,例如:
cat input | sed '/<tag1>/,/<\/tag1>/d'
但是有一个问题,命令不能正常运行,输出中去掉了一行tag1
标签后的数据。上述命令的输出:
data1
data2
data3
所以,主要问题是,我们如何删除两个 strings/tags/patterns 之间的数据,即使它们是单行或多行数据?
谢谢
- 由于
sed
无法解析xml文件,所以sed
有很多情况
效果不佳(例如评论标签中的标签)。
- 由于
sed
正则表达式不支持非贪婪匹配,我们需要
考虑变通办法。
基于以上,请你试试:
sed $'s/<tag1>/&\\n/g' input | sed '/<tag1>/,/<\/tag1>/d'
输出:
data1
data2
data3
data4
data5
第一个 sed
只是在 <tag1>
之后换行。
虽然它适用于提供的示例,但请注意有
许多情况下效果不佳(例如缺少 </tag1>
)。
注:cat input | sed SCRIPT
没用,直接sed SCRIPT input
。让我们假设:
- 你使用 GNU sed,
- 您可能还有其他标签(例如,
<tag2>
),
- 您可能在同一行上有多个组 (
a<tag1>b</tag1>c<tag1>d</tag1>e
),
- 您没有嵌套组 (
<tag1>a<tag1>b</tag1>c</tag1>
),
- 你所有的
<tag1>
和 </tag1>
都得到了适当的平衡。
GNU sed 有简洁的 -z
选项,将 NUL 字符视为行终止符,而不是换行符。因此,由于您的输入文件不包含任何 NUL 字符,因此可以将其内容视为一个字符串(其中包含换行符)。
因此,我们可以开始删除 <tag1>...</tag1>
个组,而无需考虑它们是否在同一“行”上。但是由于 sed 是贪婪的,我们不能简单地 s#<tag1>.*</tag1>##g
因为它会删除第一个 <tag1>
和最后一个 </tag1>
之间的所有内容:如果你有多个组,它也会删除组之间的文本.
然而,我们可以循环执行两个替代命令:一个删除空组 <tag1></tag1>
,然后一个删除 <tag1>
之后的任何单个字符,并且只要删除单个字符就重复:
$ cat input
<tag1>
junk1
junk2
</tag1>
data1
data2<tag1>junk3</tag1>data3<tag1>junk4</tag1>data4
data5
<tag1>junk5</tag1>
data6
<tag1>junk6</tag1>
$ sed -Ez ':a;s#<tag1></tag1>##g;s#(<tag1>).##g;ta' input
data1
data2data3data4
data5
data6
说明::a
是一个标签,用于循环。 s#<tag1></tag1>##g
删除所有空组。 s#(<tag1>).##g
删除 <tag1>
之后的任何单个字符。如果先前的替换成功,ta
分支到标签 a
。换句话说,我们循环直到没有
换人;在每次迭代中,我们删除所有空组并删除所有非空 <tag1>
、</tag1>
对之间的一个字符。当我们停止时,所有的组都被删除了。
如果它留下的空行也应该被删除,我们只需添加一个删除所有空“行”的最后命令。它通过用单个换行符(或什么都没有)替换两个换行符之间(或模式 space 和换行符之间的模式 spaces (可以为空)的任何字符串来实现如果它在模式的开头 space):
$ sed -Ez ':a;s#<tag1></tag1>##g;s#(<tag1>).##g;ta;s#(\`|\n)\s*\n##g' input
data1
data2data3data4
data5
data6
删除范围之前的单行匹配可能会有所帮助,因为如果在第一个匹配之后没有找到另一个匹配,范围将匹配到文件末尾,在您的情况下,是单行匹配。
$ sed '/>[a-z0-9]*</d;/</,/>/d' input_file
data1
data2
data3
data4
data5
/>[a-z0-9]*</d
- 这里先匹配单行。如果需要,它可以精确定位,但 >
括号在这种情况下就足够了。
/</,/>/d
- 现在您的原始代码已实现,因为现在只有一个范围匹配,它删除了该范围和 returns 一切 else.Once 再次,它可以更精确使用 tag1
但在这种情况下再一次就足够了。
假设我们有一个包含以下内容的文件:
<tag1>
junk1
junk2
</tag1>
data1
data2
data3
<tag1>junk3</tag1>
data4
data5
所以,我们要删除两个字符串之间的所有数据,即 <tag1>
和 </tag1>
。我可以使用 sed
命令完成这项工作,例如:
cat input | sed '/<tag1>/,/<\/tag1>/d'
但是有一个问题,命令不能正常运行,输出中去掉了一行tag1
标签后的数据。上述命令的输出:
data1
data2
data3
所以,主要问题是,我们如何删除两个 strings/tags/patterns 之间的数据,即使它们是单行或多行数据?
谢谢
- 由于
sed
无法解析xml文件,所以sed
有很多情况 效果不佳(例如评论标签中的标签)。 - 由于
sed
正则表达式不支持非贪婪匹配,我们需要 考虑变通办法。
基于以上,请你试试:
sed $'s/<tag1>/&\\n/g' input | sed '/<tag1>/,/<\/tag1>/d'
输出:
data1
data2
data3
data4
data5
第一个 sed
只是在 <tag1>
之后换行。
虽然它适用于提供的示例,但请注意有
许多情况下效果不佳(例如缺少 </tag1>
)。
注:cat input | sed SCRIPT
没用,直接sed SCRIPT input
。让我们假设:
- 你使用 GNU sed,
- 您可能还有其他标签(例如,
<tag2>
), - 您可能在同一行上有多个组 (
a<tag1>b</tag1>c<tag1>d</tag1>e
), - 您没有嵌套组 (
<tag1>a<tag1>b</tag1>c</tag1>
), - 你所有的
<tag1>
和</tag1>
都得到了适当的平衡。
GNU sed 有简洁的 -z
选项,将 NUL 字符视为行终止符,而不是换行符。因此,由于您的输入文件不包含任何 NUL 字符,因此可以将其内容视为一个字符串(其中包含换行符)。
因此,我们可以开始删除 <tag1>...</tag1>
个组,而无需考虑它们是否在同一“行”上。但是由于 sed 是贪婪的,我们不能简单地 s#<tag1>.*</tag1>##g
因为它会删除第一个 <tag1>
和最后一个 </tag1>
之间的所有内容:如果你有多个组,它也会删除组之间的文本.
然而,我们可以循环执行两个替代命令:一个删除空组 <tag1></tag1>
,然后一个删除 <tag1>
之后的任何单个字符,并且只要删除单个字符就重复:
$ cat input
<tag1>
junk1
junk2
</tag1>
data1
data2<tag1>junk3</tag1>data3<tag1>junk4</tag1>data4
data5
<tag1>junk5</tag1>
data6
<tag1>junk6</tag1>
$ sed -Ez ':a;s#<tag1></tag1>##g;s#(<tag1>).##g;ta' input
data1
data2data3data4
data5
data6
说明::a
是一个标签,用于循环。 s#<tag1></tag1>##g
删除所有空组。 s#(<tag1>).##g
删除 <tag1>
之后的任何单个字符。如果先前的替换成功,ta
分支到标签 a
。换句话说,我们循环直到没有
换人;在每次迭代中,我们删除所有空组并删除所有非空 <tag1>
、</tag1>
对之间的一个字符。当我们停止时,所有的组都被删除了。
如果它留下的空行也应该被删除,我们只需添加一个删除所有空“行”的最后命令。它通过用单个换行符(或什么都没有)替换两个换行符之间(或模式 space 和换行符之间的模式 spaces (可以为空)的任何字符串来实现如果它在模式的开头 space):
$ sed -Ez ':a;s#<tag1></tag1>##g;s#(<tag1>).##g;ta;s#(\`|\n)\s*\n##g' input
data1
data2data3data4
data5
data6
删除范围之前的单行匹配可能会有所帮助,因为如果在第一个匹配之后没有找到另一个匹配,范围将匹配到文件末尾,在您的情况下,是单行匹配。
$ sed '/>[a-z0-9]*</d;/</,/>/d' input_file
data1
data2
data3
data4
data5
/>[a-z0-9]*</d
- 这里先匹配单行。如果需要,它可以精确定位,但 >
括号在这种情况下就足够了。
/</,/>/d
- 现在您的原始代码已实现,因为现在只有一个范围匹配,它删除了该范围和 returns 一切 else.Once 再次,它可以更精确使用 tag1
但在这种情况下再一次就足够了。