Linux三大剑客之sed

一、简介

sed英文全称是stream editor。由贝尔实验室开发,如今主流Unix/Linux操作系统上都集成了这个工具。sed由自由软件基金组织(FSF)开发和维护,并且随着GNU/Linux进行分发,通常它也称作 GNU sed。本文将按照GUN官方在线手册的内容对sed进行介绍。

二、获取帮助信息

[sed@GeekDevOps ~]$ sed --help
[sed@GeekDevOps ~]$ man sed

我们可以通过以上2种方式获取关于sed的帮助信息,也可以通过GUN网站sed在线帮助页面获取更加详细的帮助信息( https://www.gnu.org/software/sed/manual/sed.html )

三、sed的使用

3.1 综述

sed遵循简单的工作流:读取(从输入中读取某一行),执行(在某一行上执行sed命令)和显示(把结果显示在输出中)。通常sed命令这样被调用:

sed SCRIPT INPUTFILE...

例如,把文件input.txt中出现的“hello”全部替换为“world”并输出到文件output.txt中:

[sed@GeekDevOps ~]$ echo "hello world">input.txt
[sed@GeekDevOps ~]$ sed "s/hello/world/" input.txt >output.txt
[sed@GeekDevOps ~]$ cat output.txt 
world world

如果你没有指定输入文件或输入文件是“-”,sed获取到是是标准出入流的内容,以下命令是等价的:

sed 's/hello/world/' input.txt > output.txt
sed 's/hello/world/' < input.txt > output.txt
cat input.txt | sed 's/hello/world/' - > output.txt

sed把输出写到标准输出中,使用选项-i可以编辑原文件并将替换结果打印到标准输出。把输出写入到其他文件中我们也可以参阅W和s///w命令。以下命令修改了文件且不产生任何输出。

[sed@GeekDevOps ~]$ sed -i "s/hello/world/" input.txt 
[sed@GeekDevOps ~]$ cat input.txt 
world world

默认情况下sed打印所有处理过的输出(除非输入已经被诸如d之类的命令修改或删除)。使用选项-n可以不输出除指定内容之外的打印,使用p命令可以打印指定行。以下命令将只打印输入文件的第5行:

[sed@GeekDevOps ~]$ sed -n "5p" /etc/passwd
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

sed以一个长流的方式处理多个输入文件。以下例子只打印第一个文件的第一行及最后一个文件的最后一行,其实也可以理解成sed把三个文件当做一个文件来处理了,依次追加到第一个文件的后面。也可以用选项-s来逆转这种操作,即把每个文件分开来处理。

[sed@GeekDevOps ~]$ sed -n '2p;$p' /etc/selinux/config /etc/aliases /etc/passwd
# This file controls the state of SELinux on the system.
sed:x:1000:1000::/home/sed:/bin/bash
[sed@GeekDevOps ~]$ sed -ns '2p;$p' /etc/selinux/config /etc/aliases /etc/passwd
# This file controls the state of SELinux on the system.
SELINUXTYPE=targeted 
#  Aliases in this file will NOT be expanded in the header from
#root:      marc
bin:x:1:1:bin:/bin:/sbin/nologin
sed:x:1000:1000::/home/sed:/bin/bash

sed -n -s 与sed -ns这两种写法在本质上是一样的。
不加选项-e(脚本,--expression=脚本)或-f(--file,脚本文件),sed使用第一个非选项参数作为脚本,下面的例子中就是以非选项参数作为输入文件。如果选项-e或-f 被用于指定脚本,那么所有的非选项参数被当作输入文件。选项-e和-f可以联合使用,能出现多次(所有的单个的脚本将被连接起来成为最终有效的实例)。

[sed@GeekDevOps ~]$ echo "hello world">input.txt 
[sed@GeekDevOps ~]$ sed 's/hello/world/' input.txt >output.txt
[sed@GeekDevOps ~]$ cat output.txt 
world world
[sed@GeekDevOps ~]$ sed -e 's/hello/world/' input.txt >output.txt
[sed@GeekDevOps ~]$ sed --expression='s/hello/world/' input.txt > output.txt
[sed@GeekDevOps ~]$ cat output.txt 
world world
[sed@GeekDevOps ~]$ echo 's/hello/world/' > myscript.sed
[sed@GeekDevOps ~]$ cat myscript.sed 
s/hello/world/
[sed@GeekDevOps ~]$ sed -f myscript.sed input.txt >output.txt 
[sed@GeekDevOps ~]$ cat output.txt 
world world
[sed@GeekDevOps ~]$ sed --file=myscript.sed input.txt > output.txt
[sed@GeekDevOps ~]$ cat output.txt 
world world
3.2 命令行选项

完整格式调用sed:

sed OPTIONS... [SCRIPT] [INPUTFILE...]

sed可能被以下命令行选项调用:
--version:打印版本信息。

--help:打印简要帮助信息并退出。

-n、--quiet、--silent:只输出明确处理过的内容。

-e script 、--expression=script:指定脚本。

-f script-file、--file=script-file:指定脚本文件。

-i[SUFFIX]、--in-place[=SUFFIX]:在原文件上修改并取代原文件。这一选项暗含-s。

-l N、--line-length=N:指定“l”命令的换行期望长度。长度为0则意味着不含长换行。如果不指定,则换行长度为70。

--posix:GUN sed包含了几个扩展POSIX sed。为了简化书写这些便携脚本,这个选项禁用了这个手册所有的扩展以及附加命令。扩展中被POSIX执行的外部语法大部分接受sed程序,但是以上这些中的一些(例如在报告bug中的N命令描述的行为)事实上违反了标准。如果你需要禁用后期的扩展,你可以设置POSIXLY_CORRECT 变量为以非空值。

-b、--binary:这个选项在每个平台都是可用的,仅仅在操作系统中区分文本文件与二进制文件起到作用。

--follow-symlinks:这个选项仅在支持符号连接且对选项-i被指定有影响的情况下可用。在这种情况下,如果被指定文件在命令行中是一个符号连接,sed将跟随连接并编辑连接的最终目标文件。默认行为是中断符号连接,因此连接目标将不被修改。

-E、-r:扩展正则表达式。

-s、--separate:在上文中已经举过例子,当处理多个输入文件,一般会被合并成一个文件来处理。使用这个选项则可以让sed把各个文件分开单独处理。

--sandbox:沙盒,在沙盒模式,e/w/r命令被拒绝-如果程序包含它们则还没运行时它们将丢弃。沙盒模式确保了sed在输入文件上的操作运行在指定的命令行上,而不能运行在外部命令上。

-u、--unbuffered:从输入文件读取最少的数据,更频繁的刷新输出。这将特别有用,特别像在tail -f这种情况下,能够让我们尽快地看出输出结果。

-z、--null-data、--zero-terminated:把输出看作一个一个被0字节(如:ASCII中的“NULL”)的中断符代替换行的集合。这个选项常和“sort -z”和“find -print0”一起使用。

3.3退出状态

0:成功结束。
1:无效命令、无效语法、无效正则表达式、无效的带--posix的sed扩展命令
2:一个或多个被指定的输入文件不能在命令行中被打开。
4:I/O错误或运行时一个严重进程错误,GUN sed 被立即终止。

四、sed脚本

4.1 sed脚本综述

sed命令遵循以下语法:
[addr]X[options]
X是一个单个字母sed命令。[addr]是一个可选的行地址。如果[addr]是被指定的,那么命令X在匹配的行将被执行。[addr]可以使用一个单独的行号、正则表达式、或行的范围。附加[options]被用于一些sed命令。
以下例子删除了GeekDevOps.txt的第3到5行,3,5是一个地址范围,d是删除命令:

[sed@GeekDevOps ~]$ sed "3,5d" GeekDevOps.txt >GeekDevOps.txt.new
[sed@GeekDevOps ~]$ diff GeekDevOps.txt GeekDevOps.txt.new
3,5d2
<         user_input = raw_input("Enter a positive integer to guess: ")
<         if len(user_input)==0 or not user_input.isdigit():
<             print "Not a positive integer!"

以下例子将打印所有以"def"开头的行后退出,退出代码为42,如果没有查找到或者遇到其他错误,退出状态代码将为0(为了节约篇幅,不举例),/^def是一个正则表达式地址,q是退出命令,42是命令选项:

[sed@GeekDevOps ~]$ sed "/^def/q42" GeekDevOps.txt
def guess_my_number(n):
[sed@GeekDevOps ~]$ echo $?
42

多个脚本或脚本文件可以使用选项-e或-f分别指定,如不分别指定的话应该以分号。

[sed@GeekDevOps ~]$ sed -e 's/while/do/;/^def/d' GeekDevOps.txt>GeekDevOps.txt.new
[sed@GeekDevOps ~]$ cat GeekDevOps.txt.new
    do True:
        user_input = raw_input("Enter a positive integer to guess: ")
    ...
[sed@GeekDevOps ~]$ sed -e "s/while/for/" -e '/^def/d' GeekDevOps.txt>GeekDevOps.txt.new
[sed@GeekDevOps ~]$ cat GeekDevOps.txt.new
    for True:
...
[sed@GeekDevOps ~]$ echo "s/while/do_for/">test.sed
[sed@GeekDevOps ~]$ sed -f test.sed -e '/^def/d' GeekDevOps.txt>GeekDevOps.txt.new
[sed@GeekDevOps ~]$ cat GeekDevOps.txt.new
    do_for True:
...

命令a,c,i由于他们的语法原因,不能以分号作为命令分隔符。

4.2 sed命令概述

以下命令在GUN sed是被支持的。一些是标准的POSIX命令,其他的是GUN扩展命令。(此部分原文内容较多,不一一介绍,截取常用部分进行介绍)

a\

文本

在一行后面追加文本。

a 文本

在一行后面追加文本(选择性语法)。

b label

分支无条件标签。在下一个循环开始时可能被省略。

c\
文本

在原位置替换。

c 文本

在原位置替换(选择性语法)。

d

删除模式空间,立即开始下一循环。

i\

文本

在某行之前插入。

i 文本

在某行之前插入(选择性语法)。 

q[exit-code]

没有任何需要处理的命令或输出退出sed。
(quit) Exit sed without processing any more commands or input.

Q[exit-code]

类似于q,但是不打印模式空间的内容。具体我们看一下以下例子:
[sed@GeekDevOps ~]$ sed -e '/^def/q23' GeekDevOps.txt
def
[sed@GeekDevOps ~]$ echo $?
23
[sed@GeekDevOps ~]$ sed -e '/^def/Q23' GeekDevOps.txt
[sed@GeekDevOps ~]$ echo $?
23

退出代码都一样,区别就是q有打印输出,Q没有输出。

五、用法举例

5.1
[sed@GeekDevOps ~]$ seq 6|sed '1d
> 2d
> 5d'
3
4
6

等价于:

[sed@GeekDevOps ~]$ seq 6|sed '1d;3d;5d;'
2
4
6

在命令行中,所有的sed命令通过换行来指定,也可以通过分号来指定。

5.2
[sed@GeekDevOps ~]$ seq 4 | sed '{1d;3d}'
2
4
[sed@GeekDevOps ~]$ seq 6|sed '{1d;3d};5d'
2
4
6
[sed@GeekDevOps ~]$ seq 6|sed '{1,3d};5d'
4
6

命令{},b,t,T,: 等是可以被分号隔开的。

5.3

替换指定行的内容。以下例子将在第5行中把hired替换成success:

[sed@GeekDevOps ~]$ sed '5s/hired/success/' GeekDevOps.txt | nl
     1  My father was a self-taught mandolin player. He was one of the best string instrument players in our town. He could not read
     2  music, but if he heard a tune a few times, he could play it. When he was younger, he was a member of a small country music band.
     3  They would play at local dances and on a few occasions would play for the local radio station. He often told us how he had
     4  auditioned and earned a position in a band that featured Patsy Cline as their lead singer. He told the family that after he was
     5  success he never went back. 

以下例子中,把包含music的行中的play替换成enjoy。

[sed@GeekDevOps ~]$ sed '/music/s/play/enjoy/' GeekDevOps.txt |nl
     1  My father was a self-taught mandolin player. He was one of the best string instrument players in our town. He could not read
     2  music, but if he heard a tune a few times, he could enjoy it. When he was younger, he was a member of a small country music band.
     3  They would play at local dances and on a few occasions would play for the local radio station. He often told us how he had

以下例子中,把1-3行中的he全部替换成one,如果后面不加g,则只替换第一个匹配的。

[sed@GeekDevOps ~]$ sed "1,3s/he/one/g" GeekDevOps.txt
My fatoner was a self-taught mandolin player. He was one of tone best string instrument players in our town. He could not read
music, but if one oneard a tune a few times, one could play it. Wonen one was younger, one was a member of a small country music band.
Toney would play at local dances and on a few occasions would play for tone local radio station. 

不含指定部分才进行匹配替换。以下例子中把不含单词apple的行中的hello替换成Hello,感叹号“!”取反,指定行的范围也是一样的操作。

[sed@GeekDevOps ~]$ sed '/apple/!s/hello/Hello/' test.txt
Hello world!This is my apple.Don't touch it.
hello world!This is my apple.Don't touch it.
Hello world!This is my food.Don't touch it.
5.4

sed也是支持正则表达式的。以下例子匹配了/etc/passwd文件中以bash结尾的行。

[sed@GeekDevOps ~]$ sed -n '/bash$/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
sed:x:1000:1000::/home/sed:/bin/bash

以下三种写法都是等价的,只是分隔符不一样而已,如果正则表达式或分隔符本身就包含斜杠的话,那么是需要进行转义处理的。

[sed@GeekDevOps ~]$ sed -n '/^\/home\/GeekDevOps/p' test.txt
/home/GeekDevOps
[sed@GeekDevOps ~]$ sed -n '\;^/home/GeekDevOps;p' test.txt
/home/GeekDevOps
[sed@GeekDevOps ~]$ sed -n '\%^/home/GeekDevOps%p' test.txt
/home/GeekDevOps

地址也可以用范围来表示,例如:

[sed@GeekDevOps ~]$ nl test.txt |sed -n '2,+1p'
2  hello world!This is my apple.Don't touch it.
3  hello world!This is my food.Don't touch it.

接下来再看一个扩展正则表达式的例子。本例中匹配到的是标准输出流中的单词,把每个单词中的字符全部替换成X。如果表达式中的X换成XY的话,那么会将标准输出流中的每个字符换成XY。

[sed@GeekDevOps ~]$ echo 'GeekDevOps is a very useful media !' |sed 's/\w/X/g'
XXXXXXXXXX XX X XXXX XXXXXX XXXXX !

下面的例子中,会在每个单词的前面添加一个“#”,如果需要在每个单词的后面添加内容,那么表达式应为:\>

[sed@GeekDevOps ~]$ echo 'GeekDevOps is a very useful media !' |sed 's/\

关于sed的介绍就到此为止吧,!写了许多,感觉自己都搞得晕乎乎的,掌握以上内容一般的文档处理应该是没有问题了!

你可能感兴趣的:(Linux三大剑客之sed)