为什么 n 而不是 b 或 d 或什么都不改变 sed 在此脚本中的行为?

Why does an n instead of b or d or nothing change the behaviour of sed in this script?

在回答问题 时,我在 sed 中遇到了一个我无法解释的行为 — 你能解释吗?

数据文件:data

Goodbye

select *   
from dep  
where jkdsfj  

select *   
from sal   
where jkdsfj  

select elephants
from abject poverty
join flying tigers
where abelone = shellfish;

select mouse
from toolset
join animals where tail = cord
and buttons = legs

Hello

objective 是 select 单词 fromwhere 之间的文本。

这里有脚本的 4 个变体:

这些都适用于 BSD (Mac OS X) sed 和 GNU sed。最后一个脚本可以使用 b; },它可以与 GNU sed 一起工作,但 BSD sed 拒绝它。

问题是 script.17 的输出与其他 3 个不同,我不明白为什么:

$ sed -n -f script.16 data
dep  
sal   
abject poverty
join flying tigers
toolset
join animals
$ sed -n -f script.17 data
dep  
select *   
abject poverty
toolset
and buttons = legs
Hello
$

为什么输出的是 select *and buttons = legsHello

$ sed -n -f script.18 data
dep  
sal   
abject poverty
join flying tigers
toolset
join animals
$ sed -n -f script.19 data
dep  
sal   
abject poverty
join flying tigers
toolset
join animals
$ 

为什么 n 的使用会像这样改变 sed 的行为?从我尝试使用诊断 'printing' 的一些变体来看,似乎 n 阻止 sed 在正确看到 where 时进行识别,但是 bd 都跳转到下一个循环,就像 n 通常那样,但有些不同。

鉴于两个独立的实现做同样的事情,我不得不假设这是故意的,但是......为什么?

总结

问题在于范围以及评估范围时模式 space 中的内容。

sed 中的范围端点在评估范围时与模式 space 的内容相匹配,而不是相对于原始输入行。因此,对于 sed -n '/start/,/end/{...}',重要的是命令开头的模式 space 中的内容,而不是命令处理后的模式 space 中的内容或 [=17] =] 已导致读取更多行。

简单示例

p;n 与范围结合的问题可以用更简单的代码来说明。请注意,与 bd 不同,命令 n 读取一行。因此,sed -n 'p;n' 每隔一行打印一次。例如:

$ seq 5 | sed -n 'p;n'
1
3
5

现在,结合范围观察 p;n

$ seq 5 | sed -n '/1/,/3/{p;n;}'
1
3

以上按预期工作。然而,以下内容令人惊讶:

$ seq 5 | sed -n '/1/,/2/{p;n;}'
1
3
5

包含2的行被n命令读入,然后立即被丢弃。当评估范围 /1/,/2/ 时,包含 2 的行不会出现在模式 space 中。因此,sed 永远不会看到 /1/,/2/ 的结尾,它会继续认为它在范围内。

脚本 17

现在,让我们考虑您的脚本 17,稍作修改:

sed -n '/from/,/where/ { s/.*from */BEGIN/; s/ *where.*/END/; /^ *$/d; p; n; }' data
BEGINdep  
select *   
END
BEGINabject poverty
END
BEGINtoolset
and buttons = legs
Hello

在这里,我们看到 /from/,/where/ 的范围从出现 from 到下一次 where 出现在模式缓冲区 开始处评估范围时的命令 n 读取的 where 实例永远不会结束范围。

进一步演示

考虑范围 /1/,/END/,其中 END 从未出现在文件中:

$ seq 5 | sed -n 's/3/END/; /1/,/END/{p;n}'
1
END

尽管 END 从未出现在文件中,但在评估范围时它出现在模式 space 中。因此,它结束了范围。

作为再一次演示,让我们改变一下上述命令的顺序。在下面,我们看到 END 并没有结束范围,尽管它被打印出来了:

$ seq 5 | sed -n ' /1/,/END/{s/3/END/; p; n}'
1
END
5

这是因为计算范围时 END 不在模式 space 中。因此,sed 永远不会看到范围的尽头。