为什么在这个脚本中,n而不是b或d或者什么都没有改变sed的行为?

为什么在这个脚本中,n而不是b或d或者什么都没有改变sed的行为?,sed,Sed,在为这个问题寻找答案时,我在sed中遇到了一个我无法解释的行为-你能解释吗 数据文件:数据 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 a

在为这个问题寻找答案时,我在
sed
中遇到了一个我无法解释的行为-你能解释吗

数据文件:
数据

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
目标是在单词
from
where
之间选择文本

以下是脚本的4种变体:

  • script.16

    /from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p;    }
    
  • script.17

    # Bust by final n;
    /from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; n; }
    
  • script.18

    /from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; d; }
    
  • script.19

    /from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; b
    }
    
这些都可以与BSD(MacOSX)
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*
和按钮=legs
Hello

$ 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
的行为?从我尝试的诊断“打印”的一些变体来看,
n
似乎阻止了
sed
在正确地看到
的位置时识别,但是
b
d
都跳到下一个周期,就像
n
正常工作一样,但有些不同

鉴于两个独立的实现做相同的事情,我不得不假设这是有意为之,但是……为什么?

Summary 问题在于范围以及评估范围时模式空间中的内容

sed中的范围端点在计算范围时与模式空间的内容相匹配,而不是与原始输入行相匹配。因此,对于
sed-n'/start/,/end/{…}'
,重要的是命令开始时模式空间中的内容,而不是在处理命令或
n
导致读取更多行之后模式空间中的内容

简单例子
p问题;n
结合一个范围可以用更简单的代码来说明。请注意,与
b
d
不同,命令
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
的行不会出现在模式空间中。因此,
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
读取的
实例,其中
从不结束一个范围

进一步的论证 考虑范围
/1/,/END/
,其中
END
never出现在文件中:

$ seq 5 | sed -n 's/3/END/; /1/,/END/{p;n}'
1
END
即使
END
nevers出现在文件中,它也会在计算范围时出现在模式空间中。因此,它结束了这个范围

作为另一个演示,让我们更改上述命令的顺序。下面,我们看到,
END
虽然被打印出来,但并没有结束该范围:

$ seq 5 | sed -n ' /1/,/END/{s/3/END/; p; n}'
1
END
5
这是因为计算范围时,
END
不在模式空间中。因此,
sed
永远看不到范围的尽头