Linux shell 之 sed 命令详解 第二部分 Linux shell 之 sed 命令详解 第二部分

  目录:

    一、更多的替换选项

      1.1 替换标记

      1.2 替换字符

    二、使用地址

      2.1 数学方式的行寻址

      2.2 使用文本模式过滤器

      2.3 命令组合

    三、删除行

    四、插入和附加文本

  如果你将第一部分的实践了,你会发现第一部分中介绍的 sed 的用法是有些缺陷

一、更多的替换选项

  你已经懂得了如何用s命令( substitute)来在行中替换文本。这个命令还有另外一些选项能让事情变得更为简单。

1. 替换标记

  关于替换命令如何替换字符串中所匹配的模式需要注意一点。看看下面这个例子中会出现什么情况。

1 cat data2.txt
2 This is a test of the test script.
3 This is the second test of the test script.
4 
5 $ sed 's/test/trial/' data2.txt
6 This is a trial of the test script.
7 This is the second trial of the test script.
8 $

  替换命令在替换多行中的文本时能正常工作,但默认情况下它只替换每行中出现的第一处。要让替换命令能够替换一行中不同地方出现的文本必须使用替换标记( substitution flag)。替换标记会在替换命令字符串之后设置。

s/pattern/replacement/flags

  有4种可用的替换标记:

    ⭐  数字,表明新文本将替换第几处模式匹配的地方;

    ⭐  g,表明新文本将会替换所有匹配的文本;

    ⭐  p,表明原先行的内容要打印出来;

    ⭐  w file,将替换的结果写到文件中。

  在第一类替换中,可以指定 sed 编辑器用新文本替换第几处模式匹配的地方。

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

  将替换标记指定为 2 的结果就是: sed 编辑器只替换每行中第二次出现的匹配模式。 g 替换标记使你能替换文本中匹配模式所匹配的每处地方。

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

  p 替换标记会打印与替换命令中指定的模式匹配的行。这通常会和 sed 的 -n 选项一起使用。

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

1 $ sed 's/test/trial/w test.txt' data3.txt
2 Thiss is a trial line.
3 This is a different line.
4 $
5 $
6 $ cat test.txt
7 Thiss is a trial line.
8 $

  sed 编辑器的正常输出是在 STDOUT 中,而只有那些包含匹配模式的行才会保存在指定的输出文件中。

2. 替换字符

  有时你会在文本字符串中遇到一些不太方便在替换模式中使用的字符。 Linux 中一个常见的例子就是正斜线( /)。

  替换文件中的路径名会比较麻烦。比如,如果想用 C shell 替换 /etc/passwd 文件中的 bash shell,必须这么做:

1 $ sed -n 's//bin/bash//bin/dash/p' /etc/passwd
2 root:x:0:0:root:/root:/bin/dash
3 zhengchuanyu:x:1000:1000:zhengchuanyu,,,:/home/zhengchuanyu:/bin/dash
4 $
5 $
6 $ sed -n 's!/bin/bash!/bin/dash!p' /etc/passwd
7 root:x:0:0:root:/root:/bin/dash
8 zhengchuanyu:x:1000:1000:zhengchuanyu,,,:/home/zhengchuanyu:/bin/dash
9 $

  /etc/passwd 目录下行数太多,因此现学现用,加上 选项 -n  替换标记 p 在终端中显示被修改的项

  在第二个例子中,感叹号被用作字符串分隔符,这样路径名就更容易阅读和理解了。

二、使用地址

  默认情况下,在 sed 编辑器中使用的命令会作用于文本数据的所有行。如果只想将命令作用于特定行或某些行,则必须用行寻址( line addressing)。

  在 sed 编辑器中有两种形式的行寻址:

    ⭐  以数字形式表示行区间

    ⭐  用文本模式来过滤出行

  两种形式都使用相同的格式来指定地址:

    [address]command

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

    address {
      command1
      command2
      command3
    }

  sed 编辑器会将指定的每条命令作用到匹配指定地址的行上。

1. 数字方式的行寻址

  当使用数字方式的行寻址时,可以用行在文本流中的行位置来引用。 sed编辑器会将文本流中的第一行编号为 1,然后继续按顺序为接下来的行分配行号。

  在命令中指定的地址可以是单个行号,或是用起始行号、逗号以及结尾行号指定的一定区间范围内的行。这里有个 sed 命令作用到指定行号的例子。

1 $ cat data1.txt
2 the quick brown fox jumps over the lazy dog.
3 The quick brown fox jumps over the lazy dog.
4 The quick brown fox jumps over the lazy dog.
5 The quick brown fox jumps over the lazy dog.
6 $
7 $ sed -n '2s/dog/cat/p' data1.txt
8 The quick brown fox jumps over the lazy cat.
9 $

  sed 编辑器只修改地址指定的第二行的文本。这里有另一个例子,这次使用了行地址区间。

1 $ sed -n '2,3s/dog/cat/p' data1.txt
2 The quick brown fox jumps over the lazy cat.
3 The quick brown fox jumps over the lazy cat.
4 $

  如果想将命令作用到文本中从某行开始的所有行,可以用特殊地址——美元符。

1 $ sed -n '2,$s/dog/cat/p' data1.txt
2 The quick brown fox jumps over the lazy cat.
3 The quick brown fox jumps over the lazy cat.
4 The quick brown fox jumps over the lazy cat.
5 $

  可能你并不知道文本中到底有多少行数据,因此美元符用起来通常很方便。

2. 使用文本模式过滤器

  另一种限制命令作用到哪些行上的方法会稍稍复杂一些。 sed 编辑器允许指定文本模式来过滤出命令要作用的行。格式如下:

    /pattern/command

  必须用正斜线将要指定的 pattern 封起来!!!。 sed 编辑器会将该命令作用到包含指定文本模式的行上。

1 $ grep -n "zhengchuanyu" /etc/passwd
2 40:zhengchuanyu:x:1000:1000:zhengchuanyu,,,:/home/zhengchuanyu:/bin/bash
3 $ sed -n '/zhengchuanyu/s/bash/hello/p' /etc/passwd
4 zhengchuanyu:x:1000:1000:zhengchuanyu,,,:/home/zhengchuanyu:/bin/hello
5 $

  该命令只作用到匹配文本模式的行上。虽然使用固定文本模式能帮你过滤出特定的值,就跟上面这个用户名的例子一样,但其作用难免有限。 sed 编辑器在文本模式中采用了一种称为正则表达式( regular expression)的特性来帮助你创建匹配效果更好的模式。

  正则表达式允许创建高级文本模式匹配表达式来匹配各种数据。这些表达式结合了一系列通配符、特殊字符以及固定文本字符来生成能够匹配几乎任何形式文本的简练模式。正则表达式是shell脚本编程中令人心生退意的部分之一,后面的博客中将会详细介绍相关内容。

3. 命令组合

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

1 $ sed -n '2{
2 > s/fox/elephant/p
3 > s/dog/cat/p
4 > }' data1.txt
5 The quick brown elephant jumps over the lazy dog.
6 The quick brown elephant jumps over the lazy cat.
7 $

  两条命令都会作用到该地址上。当然,也可以在一组命令前指定一个地址区间。

 1 $ sed -n '2,${
 2 > s/fox/elephant/p
 3 > s/dog/cat/p
 4 > }' data1.txt
 5 The quick brown elephant jumps over the lazy dog.
 6 The quick brown elephant jumps over the lazy cat.
 7 The quick brown elephant jumps over the lazy dog.
 8 The quick brown elephant jumps over the lazy cat.
 9 The quick brown elephant jumps over the lazy dog.
10 The quick brown elephant jumps over the lazy cat.
11 $

  sed 编辑器会将所有命令作用到该地址区间内的所有行上。

 三、删除行

  文本替换命令不是 sed 编辑器唯一的命令。如果需要删除文本流中的特定行,可以用删除命令。
  删除命令d名副其实,它会删除匹配指定寻址模式的所有行。使用该命令时要特别小心,如果你忘记加入寻址模式的话,流中的所有文本行都会被删除。

1 $ cat data1.txt
2 the quick brown fox jumps over the lazy dog.
3 The quick brown fox jumps over the lazy dog.
4 The quick brown fox jumps over the lazy dog.
5 The quick brown fox jumps over the lazy dog.
6 $ sed 'd' data1.txt
7 $

  当和指定地址一起使用时,删除命令显然能发挥出最大的功用。可以从数据流中删除特定的文本行,通过行号指定:

 1 $ cat data4.txt
 2 This is line number 1
 3 This is line number 2
 4 This is line number 3
 5 This is line number 4
 6 This is line number 5
 7 This is line number 6
 8 $
 9 $ sed '3d' data4.txt
10 This is line number 1
11 This is line number 2
12 This is line number 4
13 This is line number 5
14 This is line number 6
15 $

  或者通过特定行区间指定: 

 1 $ sed '2,3d' data4.txt
 2 This is line number 1
 3 This is line number 4
 4 This is line number 5
 5 This is line number 6
 6 $

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

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

  sed 编辑器的模式匹配特性也适用于删除命令。

1 $ sed '/number 1/d' data4.txt
2 This is line number 2
3 This is line number 3
4 This is line number 4
5 This is line number 5
6 This is line number 6
7 $

  sed编辑器会删掉包含匹配指定模式的行。 

记住, sed 编辑器不会修改原始文件。你删除的行只是从 sed 编辑器的输出中消失了。原始文件仍然包含那些“删掉的”行。

  也可以使用两个文本模式来删除某个区间内的行,但这么做时要小心。你指定的第一个模式会“打开”行删除功能,第二个模式会“关闭”行删除功能。 sed编辑器会删除两个指定行之间的所有行(包括指定的行)。

1 $ sed '/1/,/3/d' data4.txt
2 This is line number 4
3 This is line number 5
4 This is line number 6
5 $

  除此之外,你要特别小心,因为只要sed编辑器在数据流中匹配到了开始模式,删除功能就会打开。这可能会导致意外的结果。

 1 $ cat data5.txt
 2 This is line number 1.
 3 This is line number 2.
 4 This is line number 3.
 5 This is line number 4.
 6 This is line number 5.
 7 This is line number 6.
 8 This is line number 1 again.
 9 This is text you want to keep.
10 This is the last line in the file.
11 $
12 $ sed '/1/,/3/d' data5.txt
13 This is line number 4.
14 This is line number 5.
15 This is line number 6.
16 $

  第二个出现数字 “1” 的行再次触发了删除命令,因为没有找到停止模式,所以就将数据流中的剩余行全部删除了。当然,如果你指定了一个从未在文本中出现的停止模式,显然会出现另外一个问题。

1 $ sed '/1/,/8/d' data5.txt
2 $

  因为删除功能在匹配到第一个模式的时候打开了,但一直没匹配到结束模式,所以整个数据流都被删掉了。

四、插入合附加文本

  跟其他编辑器类似, sed编辑器允许向数据流插入和附加文本行。两个操作的区别可能比较让人费解:

    ⭐  插入( insert)命令( i)会在指定行前增加一个新行;

    ⭐  附加( append)命令( a)会在指定行后增加一个新行。

  这两条命令的费解之处在于它们的格式。它们不能在单个命令行上使用。你必须指定是要将行插入还是附加到另一行。格式如下:

    sed '[address]command

    new line'

  new line 中的文本将会出现在 sed 编辑器输出中你指定的位置。记住,当使用插入命令时,文本会出现在数据流文本的前面。

1 $ echo "Test line 2" | sed 'iTest line 1'
2 Test line 1
3 Test line 2
4 $

  当使用附加命令时,文本会出现在数据流文本的后面。

1 $ echo "Test line 2" | sed 'aTest line 3'
2 Test line 2
3 Test line 3
4 $

  在命令行界面提示符上使用 sed 编辑器时,你会看到次提示符来提醒输入新的行数据。你必须在该行完成 sed 编辑器命令。一旦你输入了结尾的单引号, bash shell 就会执行该命令。

1 $ echo "This line 2" | sed 'i
2 > Test line 1'
3 Test line 1
4 This line 2
5 $

  这样能够给数据流中的文本前面或后面添加文本,但如果要向数据流内部添加文本呢?

  要向数据流行内部插入或附加数据,你必须用寻址来告诉sed编辑器你想让数据出现在什么位置。可以在用这些命令时只指定一个行地址。可以匹配一个数字行号或文本模式,但不能用地址区间。这合乎逻辑,因为你只能将文本插入或附加到单个行的前面或后面,而不是行区间的前面或后面。

  下面的例子是将一个新行插入到数据流第三行前。

 1 $ sed '3i
 2 > This is insert line.' data4.txt
 3 This is line number 1
 4 This is line number 2
 5 This is insert line.
 6 This is line number 3
 7 This is line number 4
 8 This is line number 5
 9 This is line number 6
10 $

  下面的例子是将一个新行附加到数据流中第三行后。

 1 $ sed '3a
 2 > This is an append line.' data4.txt
 3 This is line number 1
 4 This is line number 2
 5 This is line number 3
 6 This is an append line.
 7 This is line number 4
 8 This is line number 5
 9 This is line number 6
10 $

  它使用与插入命令相同的过程,只是将新文本行放到了指定的行号后面。如果你有一个多行数据流,想要将新行附加到数据流的末尾,只要用代表数据最后一行的美元符就可以了。

 1 $ sed '$a
 2 > This is a new line of txt.' data4.txt
 3 This is line number 1
 4 This is line number 2
 5 This is line number 3
 6 This is line number 4
 7 This is line number 5
 8 This is line number 6
 9 This is a new line of txt.
10 $

  同样的方法也适用于要在数据流起始位置增加一个新行。只要在第一行之前插入新行即可。

  要插入或附加多行文本,就必须对要插入或附加的新文本中的每一行使用反斜线,直到最后一行。

 1 $ sed '1i
 2 > This is one line of new text.
 3 > This is another line of new text.' data4.txt
 4 This is one line of new text.
 5 This is another line of new text.
 6 This is line number 1
 7 This is line number 2
 8 This is line number 3
 9 This is line number 4
10 This is line number 5
11 This is line number 6
12 $