sed基础

Linux 中可以使用强大的编辑器如 vimemacs 来编辑文本文件。但有时候,我们只是需要自动的处理文本文件,而不是这些强大的交互式文本编辑器。这种时候可以使用 Linuxsedgawk。它们能够轻松的实现自动格式化、插入、修改或者删除文本元素。

sed 编辑器

sed 编辑器被称为流编辑器(stream editor), 它使用预先提供的一组规则来编辑数据流。规则就是提供给 sed 的命令,这些命令要么从命令行输入,要么存储在一个命令行文件中。根据所提供的命令,sed 执行如下操作:

  • 一次从输入中读取一行数据
  • 根据提供的命令匹配数据
  • 按照命令修改流中的数据
  • 将新的数据输出到 STDOUT

在流编辑器将所有命令与一行数据匹配完毕后,就会重复这个流程匹配下一行数据。在处理完所有的流数据后,它就会终止。由于命令是逐行执行的,所以只需要对数据流进行一遍处理就可以完成编辑操作,这比交互式编辑器快的多,而且是完全自动化的操作数据。
sed 的命令格式如下:

sed options script file

选项可以修改 sed 命令的行为,可以使用的选项如下表:

options desc
-e script script 中的命名添加到已有的命令中
-f file file 中的命令添加到已有的命令中
-n 不产生命令输出,使用 print 来完成输出

1. 在命令行定义编辑器命令

默认情况下,sed 编辑器直接从 STDIN 读取流数据,然后匹配命令执行编辑操作。如下:

$ echo "This is a test" | sed 's/test/big test/'
This is a big test
$

这个例子使用了 s 命令。s 命令是字符串替换命令。上面的例子是单行数据,sed 也可以处理多行数据。

$ cat data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$
$ sed `s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
$

sed 几乎是瞬间就执行完成并返回数据。并且 sed 并不会修改原始文件。如果查看原来的文件,它仍然保留这原始数据

$ cat data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$

2. 在命令行使用多个编辑器命令

要在命令行上使用多个 sed 命令,使用 -e 选项就可以了。

$ sed -e 's/brown/green/; s/dog/cat/' data1.txt
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
$

上面的2个命令都会作用到每一行中。使用多个命令时命令之间必须用分号分开,并且在命令末尾和分号之间不能有空格。如果不想使用分号,也可以使用bash中的次提示符来分隔命令。只有输入第一个单引号标示出sed程序脚本的起始,bash就会继续提示你输入更多命令,直到输入了标示结束的单引号。

$ sed -e '
> s/brown/green/
> s/dog/cat/' data1.txt
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
$

结果和单行输入的命令一样。必须记住,要在末尾单引号所在行结束命令。bash一旦发现了结束的单引号就会执行命令。

3. 从文件中读取编辑器命令

如果有大量的sed需要处理,把它们放到一个单独的文本文件中是个不错的选择。可以在sed命令中使用- f来制定命令文件.

$ cat script1.sed
s/brown/green/
s/fox/elephant/
s/dog/cat/
$
$ sed -f script1.sed data1.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
$

在这种情况下,不用在每条命令后面放置分号。sed从指定的文件中读取命令,并把它们应用到数据文件的每一行上面。

sed基础

成功使用sed的关键在于掌握各式各样的命令和格式。下面就来介绍下sed中的各个基本的命令和功能。

1. 替换命令

1.1 替换标记

默认情况下sed只替换每行中第一处匹配的字符串。如果想替换一行中不同的地方就需要使用替换标记(substitution flag)。替换标记在替换命令之后设置。

s/pattern/replacement/flags

有一下4中可以使用的替换标记:

  • 数字:表明替换第几处匹配的地方
  • g: 替换所以匹配的文本
  • p: 打印原先行的内容
  • w file: 将替换的结果写到文件中

下面看看这4中标记的效果:

$ cat data2.txt
This is a test of the test script.
This is the second test of the test script.
$
$ sed 's/test/trial/' data2.txt
This is a trial of the test script.
This is the second trial of the test script.
$

原始的不使用替换标记的命令,上面的输出中只替换了每行第一个匹配到的test

$ sed 's/test/trial/2' data2.txt
This is a test of the trial script.
This is the second test of the trial script. $
$

使用数字2来当作替换标记,替换了每行中第二个匹配到的test

$ sed 's/test/trail/g' data2.txt
This is a trial of the trial script.
This is the second trial of the trial script.
$

g标记会替换所以匹配到的字符串.

$ cat data3.txt
This is a test line.
This is a different line.
$
$ sed -n 's/test/trial/p' data3.txt This is a trial line.
$

p标记会打印匹配到的行。通常会和-n选项一起使用。

w替换标记会产生同样的输出,不过会将输出保存到指定文件中。

$ sed 's/test/trial/w test.txt' data3.txt
This is a trial line.
This is a different line.
$
$ cat test.txt
This is a trial line.
$

sed 的正常输出是在STDOUT,只有那些匹配到的行才会保存到指定的文本文件中。

1.2 替换字符

有时会在字符串中遇到不太方便处理的字符,比如Linux下的/。替换路径中的/就比较麻烦,必须使用\来转义:

$ sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd

要解决这个问题,sed允许使用其它字符来作为替换命令中的字符串分隔符:

$ sed 's!/bin/bash!/bin/csh!' /etc/passwd

这个例子使用感叹号作为字符串分隔符,这样路径名就容易阅读和理解了。

2. 使用地址

默认情况下,sed中的命令会作用于所有的数据行,但是如果只想将命令作用于某些特定的行,则必须使用行寻址(line addressing)
sed中有两种行寻址:

  • 以数字形式表示行区间
  • 以文本模式来过滤出行

以上两种形式都使用相同的格式来制定行地址:

[address]command

也可以将特定地址的多个命令分组:

address {
    command1
    command2
    command3
}

sed会将特定的命令作用于指定的行上。

2.1 使用数字方式的行寻址

使用数字方式的寻找时,可以使用行在文本流中的位置来引用。文本流中第一行编号为1,然后按顺序分配行号。
在命令中使用行号时可以指定单个行号,或是用起始行号、逗号以及结尾行号指定的一定区间范围的行。

$ sed '2s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
$

上面的例子使用的是单个行号,sed只修改了第二行的内容。

$ sed '2,$s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy cat
$

上面使用了行地址区间。并且$代表从某行开始的所有行。

2.2 使用文本模式的行寻址

sed编辑器允许指定文本模式来过滤出命令有要作用的行。格式如下:

/pattern/command

必须用正斜线将制定的pattern封起来。其实就是使用正则表达式(regular expression)来过滤文本行. 正则表达式允许创建高级文本模式来匹配各种数据。这种表达式结合了一些列通配符、特殊字符以及固定文本字符来匹配几乎任何形式的文本的简练模式。

$ grep Samantha /etc/passwd
Samantha:x:502:502::/home/Samantha:/bin/bash
$
$ sed '/Samantha/s/bash/csh/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[...]
Christine:x:501:501:Christine B:/home/Christine:/bin/bash
Samantha:x:502:502::/home/Samantha:/bin/csh
Timothy:x:503:503::/home/Timothy:/bin/bash
$

上面是一个使用简单的文本模式匹配的例子。关于复杂的正则表达式的内容请看相关文章。

2.3 命令组合

如果需要在单行上执行多条命令,可以用花括号将多条命令组合在一起。sed编辑器会在地址行处执行每条命令。

$ sed '2{
> s/fox/elephant/
> s/dog/cat/
> }' data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown elephant jumps over the lazy cat.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$

两条命令都会作用在第二行。当然也可以使用地址区间:

$ sed '3,${
> s/brown/green/
> s/lazy/active/
}' data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick green fox jumps over the active dog.
The quick green fox jumps over the active dog.
$

sed 会将上面的两条命令作用于第三行之后的所有行上。

2 删除行

除了替换文本,sed也可以删除文本流中的特定行。sed得删除命令是d,但是使用该命令要非常小心,如果忘记在命令中加上寻址符的话,流中的文本都会被删除。

$ cat data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
$
$ sed 'd' data1.txt
$

当和寻址符一起使用时,sed才能发挥最大的功用。通过指定行号,sed可以从文本流中删除指定的行:

$ cat data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
$
$ sed '3d' data6.txt
This is line number 1.
This is line number 2.
This is line number 4.
$

也可以通过地址区间指定要删除的行区间:

$ sed '2,3d' data6.txt
This is line number 1.
This is line number 4.
$

或者特殊的文件结尾字符:

$ sed '3,$d' data6.txt
This is line number 1.
This is line number 2.
$

sed的寻址符也可以使用模式匹配:

$ sed '/number 1/d' data6.txt
This is line number 2.
This is line number 3.
This is line number 4.
$

还可以使用文本匹配模式来删除某个区间的所有行。但是这么做要非常小心,第一个文本模式会"打开"行删除功能,第二个模式会"关闭"行删除功能。

$ sed '/1/,/3/d' data6.txt
This is line number 4.
$

另外你也要特别小心,sed只要在文本行中匹配到了开始模式,删除功能就会打开:

$ cat data7.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
This is line number 1 again.
This is text you want to keep.
This is the last line in the file.
$
$ sed '/1/,/3/d' data7.txt
This is line number 4.
$

在第二次匹配到1后,行删除功能以及打开,因为没有在文本流中匹配到停止模式,所以删除功能就把剩余的行全部删除了。如果指定了开始模式,但是指定了一个无法匹配文本流中任何行的停止模式,这就会出现另外的问题:

$ sed '/1/,/5/d' data7.txt
$

因为删除功能在匹配到第一个模式的时候就打开了,但是没有匹配到第二个模式,所以整个文本流都被删除了。

3. 插入和增加文本

和其他编辑器类似,sed也可以向数据流中插入和添加文本行。

  • i(insert): 在指定行前面增加一个新行
  • a(append): 在指定行后面增加一个新行

这两个命令不能在单个命令行上使用。格式如下:

sed '[address]command\
new line'

new line中的文本会出现在指定的位置。当使用插入命令时,文本会出现在数据流文本的前面。

$ echo "This is a test line" | sed 'i\This is a insert line'
This is a insert line
This is a test line
$
$ echo "This is a test line" | sed 'a\This is a append line'
This is a test line
This is a append line
$

如果不指定地址,sed会在每行数据流文本前面或者后面增加或追加指定的文本。一般情况下我们只是希望在指定行的前面或者后面添加文本,这时候可以指定一个地址,可以使用数字或者匹配模式,但是不能使用地址区间。因为追加文本只能以行为单位,而不能追加到区间的前面或后面。
下面讲的例子是讲文本行添加到第3行前面和后面:

$ sed '3i\
This is an insert line.' data6.txt
This is line number 1.
This is line number 2.
This is an inserted line.
This is line number 3.
This is line number 4.
$
$ sed '3a\
This is an appended line.' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an appended line.
This is line number 4.
$

如果想在文本流的开始和结束位置添加文本行,可以使用1$来指定文本流的开始和结束,使用ia在文本流的开始和末尾添加文本。
如果想一次行添加多行文本,可以在每行后面使用反斜线,直到最后以行。

$ sed '1i\
> This is the first insert line.\
> And this is the second insert line.' data6.txt
This is the first insert line.
And this is the second insert line.
This is line number 1.
This is line number 2.
This is line number 3.
This is an appended line.
This is line number 4.
$

4. 修改行

sed中的c命令支持修改数据流中的整行文本。它跟ia的工作机制一样,必须在命令中指定新行。

$ sed '3c\
> This is a changed line.' data6.txt
This is line number 1.
This is line number 2.
This is a changed line of text.
This is line number 4.
$

c命令指出单地址也指出地址区间,也可以使用文本匹配。如果是地址区间,则会把地址区间中的文本变为指定的文本行。

$ sed '/number 1/,/number 3/c\
> This is a new line.' data6.txt
This is a new line.
this is line number 4.
$

记住:sed是一个流编辑器。

5. 转换命令

转换(transform)命令(y)是唯一一个可以处理单个字符的sed命令。格式如下:

[address]y/inchars/outchars/

转换命令会对incharsoutchars值进行一对一映射。inchars中的第一个字符会被转换为outchars中的第一个字符,inchars中的第二个字符会被转换为outchars中的第二个字符,依次类推。所有如果inchars的长度和outchars长度不同,则sed会产生一条错误消息。

$ sed 'y/123/789' data8.txt
This is line number 7.
This is line number 8.
This is line number 9.
This is line number 4.
This is line number 7 again.
This is yet another line.
This is the last line in the file.
$

转换命令是一个全局命令,也就是说它会在文本行中替换所有的指定字符,而不考虑它们出现的位置。

$ echo "There is 1 cat and 1 dog." sed 'y/123/456/'
There is 4 cat and 4 dog.
$

你无法限定只转换特定地方的特定字符,sed会转换所有匹配到的字符。

sed基础的思维导图,可以参考下图:

你可能感兴趣的:(sed基础)