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

时间:2015-04-03 06:16:19

标签: sed

在为问题How to extract content between two patterns in Unix开发答案时,我在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

目标是选择单词fromwhere之间的文字。

以下是脚本的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(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 = 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识别where何时正确显示,但bd都跳转到下一个周期,而不是像n那样正常,但有些不同。

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

1 个答案:

答案 0 :(得分:4)

摘要

问题在于范围以及评估范围时图案空间中的内容。

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

简单示例

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命令读入,然后立即被丢弃。评估范围2时,包含/1/,/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出现在命令开头的模式缓冲区中时评估范围。由where读取的n实例永远不会结束范围。

进一步示范

考虑文件中出现/1/,/END/ nevers的范围END

$ 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永远不会看到范围的结束。