1. sed 的调用格式

sed的调用格式在大多数场合下如下:

$ sed 'address command' < input_file > output_file

单引号里面的部分大致分为两部分:

  1. 地址范围部分:

    如果没有指出地址范围,则后面的command会作用到输入文件的每一行;

    如果指定了地址范围,则可能有几种情况:

    • 只有一个数字(例如:10),则command只会作用该行
    • 只有一个pattern(例如:/^#/),则command只会作用在满足pattern的那行。这里的pattern的分割符默认是/,但是可以通过加反斜杠的方式换用别的分割符,例如: /pattern/ <-> \:pattern: <-> \_pattern_
    • 有一对数字(例如:10,20),则command只会作用在第1020行(闭区间);
    • 有一对pattern(例如:/start/,/end/),则command只会作用在包含start的行至包含end的行(闭区间)。注意,逗号前的pattern令sed的flag打开,表示从这行开始要处理command,而逗号后面的pattern令sed的flag关闭,表示comamnd只作用到这行为止;
    • 如果command前加上!则表示command作用在除了前面的地址所表示的闭区间地址范围以外的行
  2. 命令部分:

    命令部分有很多,不过最广为人知的就是s/pattern/replace/[flag][command]了。这个命令是search and replace,可以在最后一个分割符后面加上flag或command:

    1. /g(flag): 替换该行上的全部匹配项
    2. /I(flag): 不区分大小写(这个也可以用于确定地址范围的pattern后面)
    3. /w file(command): 将匹配的行写到文件file中去
    4. /p(command): 将匹配的行输出

2. sed 中有多个”address command”

首先需要明确的是,由于sed是基于行进行处理的。即,sed会对输入文件的每一行进行操作,先根据地址范围来判断是否对该行执行command。如果执行,则按照commnad在命令行上的先后顺序对其进行操作(例如:print, delete, insert等等),并且前一个command将处理后的这一行内容作为输入给下一个command

例如:

1
$ sed -e 's/sunny/rainy/g' -e 's/good/bad/g' < in

上例会把in中的每一行中包括sunny的字段改成rainy,再将good的字段改成bad,输出到屏幕。

$ sed -e '/sunny/ p' -e 's/sunny/rainy/g' << EOF
sunny
EOF

上例的输出为:

sunny
rainy

之所以不是两个rainy的原因是,command1仅仅是将包含sunny的行输出到屏幕,传给command2的依然只是当前处理的这行,而不会把输出到屏幕的那行也传给command2

$ sed -e '/sunny/ p' -e '/sunny/ d' -e 's/sunny/rainy/g' << EOF
sunny
EOF

上例的输出为:

sunny

之所以没有输出command3中的rainy,是因为在command2中,该行已经被删掉了,因此传给command3的是空行,因此是不会有替换和输出的。事实上,d将所谓的__pattern space__(一个保存当前行当前内容的空间,不过这不会保存r, i, a, c所加入的内容)清空,一旦__pattern space__被清空,则后续的所有command都不会被执行。

sed 中有多个command的语法一般有几种写法:

  1. 使用-e来区分多个address command

     $ sed -e 'address1 command1' -e 'address2 command2' ... < input_file
    

    或者

     $ sed -e 'address1 command1' \
     'address2 command2' \
     ...
     'addressN commandN' < input_file
    
  2. 使用一对'符号将多对address command括起来, 但是要保证每行上只有一个address command

     $ sed 'address1 command1
     address2 command2
     ...
     addressN commandN' < input_file
    
  3. 使用{}将多个address command括起来:

     $ read header
     $ sed -e '\:#include <'"$header"'>: {
         r '"$header"'
         d
     }
    

    这种方式的好处在于,如果把它们写在脚本里面,可以方便地添加注释。例如:

     $ read header
     $ sed -e '\:#include <'"$header"'>: {
    
         # read the file
         r '"$header"'
    
         # delete everything in pattern space
         # and read next line in
         d
     }