我有一段文本块需要删除,但是,只有当它包含块内的特定文本时才需要删除:
...
<script language="JavaScript">
var somethingA = 0;
var somethingB = 0;
var somethingC = 0;
// do some stuff
</script>
<script language="JavaScript">
var somethingA = 0;
var somethingC = 0;
var somethingD = 0;
// do some stuff
</script>
....
我只想删除<script>
包含的块。文件中任意位置的块var somethingB
数量可能任意。<script>
我希望使用 sed 做类似的事情:
sed 's/<script/,/<\/script>/ D'
但是,我不知道如何仅删除var somethingB
其中的块。
附言:我也可以使用 perl 或 awk。为了保持一致性,我宁愿使用 sed,但如果使用 perl 和/或 awk 更简单,我会很快换个方式。谢谢!
答案1
如果部分解决方案vim
是可以接受的:
:%s/<script [^<]*\(\n[^<]*\)*somethingB.*\(\n[^<]*\)*<\/script>//g
但是如果其中还有其他标签,它将不起作用<script>
,因为使用[^<]
,模式可能不包含<
。
答案2
我没有简单的解决方案。实际上,它使用 awk 以类似 C 的 awk 语言编写所需的算法。假设要过滤的文本位于名为“filename”的文件中:
awk 'BEGIN { curr=0 } \
/<script .*>/ { in_block=1; del_block=0 } \
/<\/script>/ { in_block=0; blockend=1 } \
/var[[:space:]]+somethingB/ { if (in_block==1) \
{ del_block=1 } } \
{ if (in_block==0) \
{ if (blockend==0) \
# Neither in a block nor block end reached.
# Just print the line
{ print } \
else \
{ # End of a block reached. Do block end handling
# just this one time. Block end flag off
blockend=0
if (del_block==1) \
{ # delete the block. Just throw away the lines
# in the lines array
curr=0 } \
else \
{ # End of block and no delete. Print it out
for (i=0; i<curr; i++) \
{ print line[i] }
print # Print the </script> line
# use line-array for the next block
curr=0 \
} \
} \
} \
else \
{ # In a block. Save the current line for later
line[curr]=$0
curr++ } \
}' filename
(块的结束标记)的模式</script>
有点简单。它要求它完全按照那样写,没有任何空格。如果它可以包含空格,您可能希望这样写:
/<[[:space:]]*\/script[[:space:]]*>/
的模式var somethingB
是var
- 一个或多个空格 - somethingB
,这可能就是您要搜索的。如果您希望将其固定为恰好一个空格,var
则更somethingB
简单:/var somethingB/
答案3
这应该可以直接完成sed
。由于我不是sed
巫师,所以我需要运行两次。
在第一次运行中,我们准备文件以确保
<script>...</script>
块被空行包围:sed -e '/<script/i\ ' -e '/script>/a\ ' code.js
这不是什么火箭科学:
i
插入一条线前a
与模式匹配的行 附加一条线后与模式匹配的行。在两种情况下,行都仅包含一个空白。这是需要
sed
单独检测每个块的,即非贪婪地在第二步中)。第二次运行将消除其中的块
var somethingB
:sed '/<script/,/script>/{H;d;};x;/var somethingB/d'
/<script/,/script>/{H;d;}
将块移动到 sed 的保存空间(H
附加到保存空间,d
从模式空间中删除)x
将保存空间与模式空间进行交换- 如果模式
/var somethingB/
匹配,则删除(d
)包含完整块的模式空间<script>
。 最后
sed
隐式打印模式空间。我在这里引用的是Unix Sed 教程。
因此,在一个命令行中使用一个很好的管道:
sed -e '/<script/i\ ' -e '/script>/a\ ' code.js | sed '/<script/,/script>/{H;d;};x;/var somethingB/d'
如果您愿意,可以使用第三个
sed
实例来摆脱多余的空行:sed '/^ $/d'
答案4
我不知道这是否是最有效的方法,但我设法用一行sed
代码编写了它。假设您的文本块位于file.txt
:
tac file.txt | sed '/var somethingB/,/<script/{/var somethingB/p;d;}' | tac | sed '/var somethingB/,/<\/script>/d'
或者你甚至可以更加严格一点,然后执行以下操作:
tac file.txt | sed '/var somethingB = /,/^<script /{/var somethingB = /p;d;}' | tac | sed '/var somethingB = /,/^<\/script>$/d'
工作原理如下:
- 以相反的顺序读取文件:
tac file.txt
- 在第一次运行中,删除模式(“var somethingB”)和块(“<script”)开头之间的行 – 记住以相反的顺序读取文件 –。但排除包含“var somethingB”的行(第二次运行需要它):
sed '/var somethingB/,/<script/{/var somethingB/p;d;}'
- 再次反向读取文件(现在我们按正常顺序读取):
tac
- 在第二次运行中,删除模式(“var somethingB”)和块末尾(“”)之间的行:
sed '/var somethingB/,/<\/script>/d'
。请注意,我们需要用 来转义字符 /。