Linux-sed

2019-04-18 17:49:49 7354

一、简介

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.landui.com/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...]

1

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,但是不打印模式空间的内容。具体我们看一下以下例子:

1

[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'


命令{},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/\</#/g'

#GeekDevOps #is #a #very #useful #media !

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

#GeekDevOps #is #a #very #useful #media !


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



提交成功!非常感谢您的反馈,我们会继续努力做到更好!

这条文档是否有帮助解决问题?

非常抱歉未能帮助到您。为了给您提供更好的服务,我们很需要您进一步的反馈信息:

在文档使用中是否遇到以下问题: