sedstream editor(流编辑器)的缩写。它最常见的用法是进行文本替换。这则攻略中包括
了大量sed命令的常见用法。

4.5.1 实战演练

sed可以使用另一个字符串来替换匹配模式。模式可以是简单的字符串或正则表达式:

  1. $ sed 's/pattern/replace_string/' file

sed也可以从stdin中读取输入:

  1. $ cat file | sed 's/pattern/replace_string/'

如果你用的是vi编辑器,你会发现它用于替换文本的命令和sed的非常相似。sed默认只打印出被替换的文本,可以将其用于管道中。

  1. $ cat /etc/passwd | cut -d : -f1,3 | sed 's/:/ - UID: /'
  2. root - UID: 0
  3. bin - UID: 1
  4. daemon - UID: 2
  5. ...

image.png

  1. 选项-i会使得sed用修改后的数据替换原始文件:

    1. $ sed -i 's/text/replace/' file
  2. 之前的例子只替换了每行中模式首次匹配的内容。g标记可以使sed执行全局替换:

    1. $ sed 's/pattern/replace_string/g' file

    /#g标记可以使sed替换第N次出现的匹配:

    1. $ echo thisthisthisthis | sed 's/this/THIS/2g' # 替换第 2 次出现的匹配
    2. thisTHISTHISTHIS
    1. [root@dev workspace]# echo thisthisthisthis | sed 's/this/THIS/2g' # 替换第 2 次出现的匹配
    2. thisTHISTHISTHIS
    3. [root@dev workspace]# echo thisthisthisthis | sed 's/this/THIS/3g' # 替换第 3 次出现的匹配
    4. thisthisTHISTHIS
    5. [root@dev workspace]#
    6. [root@dev workspace]# echo thisthisthisthis | sed 's/this/THIS/4g' # 替换第 3 次出现的匹配
    7. thisthisthisTHIS
    8. [root@dev workspace]# echo thisthisthisthis | sed 's/this/THIS/5g' # 替换第 5 次出现的匹配
    9. thisthisthisthis
    10. [root@dev workspace]# echo thisthisthisthis | sed 's/this/THIS/1g' # 替换第 1 次出现的匹配
    11. THISTHISTHISTHIS
    12. [root@dev workspace]#

    sed命令会将s之后的字符视为命令分隔符。这允许我们更改默认的分隔符/

    1. sed 's:text:replace:g'
    2. sed 's|text|replace|g'

    如果作为分隔符的字符出现在模式中,必须使用\对其进行转义:

    1. sed 's|te\|xt|replace|g'

    \|是出现在模式中被转义的分隔符。

4.5.2 补充内容

sed命令可以使用正则表达式作为模式,另外还包含了大量可用于文本处理的选项。

移除空行

有了正则表达式的支持,移除空行不过是小菜一碟。空行可以用正则表达式^$进行匹配。最后的/d告诉sed不执行替换操作,而是直接删除匹配到的空行:

  1. $ sed '/^$/d' file

直接在文件中替换

如果将文件名传递给sed,它会将文件内容输出到stdout。要是我们想就地(in place)修改文件内容,可以使用选项-i

  1. $ sed 's/PATTERN/replacement/' -i filename

例如,使用指定的数字替换文件中所有3位数的数字:

  1. $ cat sed_data.txt
  2. 11 abc 111 this 9 file contains 111 11 88 numbers 0000
  3. $ sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt
  4. $ cat sed_data.txt
  5. 11 abc NUMBER this 9 file contains NUMBER 11 88 numbers 0000

上面的单行命令只替换了所有的3位数字。正则表达式\b[0-9]\{3\}\b用于匹配3位数字。[0-9]表示数字取值范围是从09{3}表示匹配之前的数字3次。\{3\}中的\用于转义{和}\b表示单词边界。

有一种值得推荐的做法是先使用不带-i选项的sed命令,以确保正则表达式没有问题,如果结果符合要求,再加入-i选项将更改写入文件。另外,你也可以使用下列形式的sed

  1. sed -i.bak 's/abc/def/' file

这时的sed不仅替换文件内容,还会创建一个名为file.bak的文件,其中包含着原始文件内容的副本。

已匹配字符串标记(&)

sed中,我们可以用&指代模式所匹配到的字符串,这样就能够在替换字符串时使用已匹配的内容:

  1. $ echo this is an example | sed 's/\w\+/[&]/g'
  2. [this] [is] [an] [example]

在这个例子中,正则表达式\w\+匹配每一个单词,然后我们用[&]替换它。&对应于之前所匹配到的单词。

子串匹配标记(\1

&指代匹配给定模式的字符串。我们还可以使用\#来指代出现在括号中的部分正则表达式(注:子模式)所匹配到的内容:

  1. $ echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
  2. this is 7 in a number
  1. [root@dev workspace]# echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
  2. this is 7 in a number
  3. [root@dev workspace]#

这条命令将digit 7替换为7\(pattern\)用于匹配子串,在本例中匹配到的子串是7。子模式被放入使用反斜线转义过的()中。对于匹配到的第一个子串,其对应的标记是\1,匹配到的第二个子串是\2,往后以此类推。

  1. $ echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
  2. EIGHT seven
  1. [root@dev workspace]# echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
  2. EIGHT seven
  3. [root@dev workspace]#

([a-z]\+\)匹配第一个单词,\([A-Z]\+\)匹配第二个单词。\1\2分别用来引用这两个单词。这种引用形式叫作向后引用(back reference)。在替换部分,它们的次序被更改为\2 \1,因此就呈现出了逆序的结果。

组合多个表达式

可以利用管道组合多个sed命令,多个模式之间可以用分号分隔,或是使用选项-e PATTERN

  1. sed 'expression' | sed 'expression'

它等同于

  1. $ sed 'expression; expression'

或者

  1. $ sed -e 'expression' -e 'expression'

考虑下列示例:

  1. $ echo abc | sed 's/a/A/' | sed 's/c/C/'
  2. AbC
  3. $ echo abc | sed 's/a/A/;s/c/C/'
  4. AbC
  5. $ echo abc | sed -e 's/a/A/' -e 's/c/C/'
  6. AbC

引用

sed表达式通常用单引号来引用。不过也可以使用双引号。shell会在调用sed前先扩展双引号中的内容。如果想在sed表达式中使用变量,双引号就能派上用场了。

例如:

  1. $ text=hello
  2. $ echo hello world | sed "s/$text/HELLO/"
  3. HELLO world

$text的求值结果是hello