【linux命令学习】— sed 命令学习

我的CSDN博客地址,有目录,更好观看
https://blog.csdn.net/u013332124/article/details/83016838

一、概述

什么是sed

SED的英文全称是 Stream EDitor,是linux上一款非常好用的非交互式的文本编辑器。sed可以做这些事:

  • 文本替换
  • 选择性的输出文本文件
  • 从文本文件的某处开始编辑
  • 无交互式的对文本文件进行编辑等

sed的工作原理

SED的工作原理就是分3个步骤:读取、执行、显示。 SED会从输入流读取每一行,然后根据命令执行一些操作后,把修改后的内容发送给输出流(也就是输出到控制台)。

要注意的是,sed不会修改原来文本的内容,只会将修改后的内容输出到控制台。

二、使用姿势

1. 主要语法

sed [参数] '[address][command]' file

sed支持的参数

这些选项是GNU规范定义的,可能对于某些版本的SED并不支持。其中前3个参数是所有sed版本都支持的

  • -n–quiet, –slient:默认情况下,模式空间中的内容在处理完成后将会打印到标准输出,该选项用于阻止该行为【所有的sed版本都支持】
  • -e script–expression=script:指定要执行的命令,使用该参数,我们可以指定多个命令【所有的sed版本都支持】
  • -f script-file–file=script-file:指定包含要执行的命令的脚本文件【所有的sed版本都支持】
  • –follow-symlinks:如果提供该选项的话,在编辑的文件是符号链接时,SED将会跟随链接
  • -i[SUFFIX]–in-place[=SUFFIX]:该选项用于对当前文件进行编辑,如果提供了SUFFIX的话,将会备份原始文件,否则将会覆盖原始文件。mac上的i参数后面要跟一个字符串,表示备份文件名,可以传空,不传的话会报错。
  • -l N–line-lenght=N:该选项用于设置行的长度为N个字符
  • –posix:该选项禁用所有的GNU扩展
  • -r–regexp-extended:该选项将启用扩展的正则表达式
  • -u–unbuffered:指定该选项的时候,SED将会从输入文件中加载最少的数据,并且更加频繁的刷出到输出缓冲区。在编辑tail -f命令的输出,你不希望等待输出的时候该选项是非常有用的。
  • -z–null-data:默认情况下,SED对每一行使用换行符分割,如果提供了该选项的话,它将使用NULL字符分割行

address—行寻址

行寻址是sed使用中一个很重要的概念。行寻址主要是告诉sed后面的command命令要用于处理哪些行。如果没有指定的话则表示command命令将会被运用处理输入流的每一行。我们不仅可以通过行号来定位行,也可以通过匹配内容来定位行。如果不填的话默认就是匹配所有的行

# 没有行寻址,则输出test.txt 文本中的所有内容
# 由于在默认情况下,sed会输出输入流中的每一行,所以加上-n 禁止默认的输出
sed -n 'p' test.txt
# 输出test.txt文本中的第一行
# 执行体中的 1 表示第一行,p表示一个执行命令,就是打印这一行
sed -n '1p' test.txt
# 输出test.txt文本中的第1行到第3行的内容
sed -n '1,3p' test.txt
# 输出test.txt文本中的第3行到最后一行之间的内容,$ 表示最后一行
sed -n '3,$p' test.txt
# 输出test.txt文本中 以"hello"的行 到最后一行 之间的内容
# 这里的内容匹配可以接受正则表达式
sed -n '/^hello/,$p' test.txt
# 输出test.txt文本中 以"hello"的行 到 包含world的行 之间的内容
sed -n '/^hello/,/world/p' test.txt

command—sed支持的命令

sed支持的命令非常丰富,几乎可以满足大部分的使用场景。现在这个表的命令看不懂没关系,下面会一个个的详细介绍这些命令的使用场景和使用姿势。

命令 功能
a 在当前行后添加一行或多行。多行时除最后一行外,每行末尾需用“\”续行
c 用新文本替换当前行中的文本。多行时除最后一行外,每行末尾需用""续行
i 在当前行之前添加一行或多行。多行时除最后一行外,每行末尾需用""续行
d 删除行
D 删除多行中的第一行
p 打印行
p 打印多行中的第一行
n 读入下一输入行,并从下一条命令而不是第一条命令开始对其的处理
N 提前读入下一行
q 结束或退出sed
r 从文件中读取输入行
! 对所选行以外的所有行应用命令
s 用一个字符串替换另一个
g 在行内进行全局替换
w 将所选的行写入文件
l 输出时带隐藏字符
y 将字符替换为另一字符,对单个字符进行转换(不能对正则表达式使用y命令)
& 输出正则匹配到的内容
= 输出行号
h 把模式空间里的内容复制到暂存空间
H 把模式空间里的内容追加到暂存空间
g 把暂存空间里的内容复制到模式空间,覆盖原有的内容
G 把暂存空间的内容追加到模式空间里,追加在原有内容的后面
x 交换暂存缓冲区与模式空间的内容

-e 参数

# 先将文本的hello 转换为world。 之后在将第三行的内容删掉
sed -e 's/hello/world/' -e '3d' test.txt
# 效果等同于
sed -e 's/hello/world/;3d' test.txt

2. 各个命令的使用姿势

使用前往文本 test.txt输出以下内容

1. name,age,sex
2. hello world
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok

p — 打印行

sed 'p' test.txt 

使用上面的语句进行输出的话,我们会发现每一行都输出了两次,这是由于sed默认会输出所有的行。之后匹配每一行时,又执行了p命令,等于又输出了一次。我们可以用'-n' 参数来屏蔽sed的默认输出行为。

sed -n 'p' test.txt 

这样,文本就只会输出一次了。

P — 输出多行中的第一行

# 先用N会一下子加载两行,再用P命令输出多行中的第一行
# N 命令的解释看下面的内容
less test.txt| sed -n 'N;P'
# 输出
1. name,age,sex
3. today is 10.10
5. public class aaa

p命令会直接将模式空间内的内容全部输出来,有多少行输多少行(大多数情况下都是一行)。如果遇到模式空间有多行内容,大写P命令只会输出多行中的第一行。

关于模式空间的介绍请看下一节内容。

d — 删除行

# 删除第一行
sed '1d' test.txt
# 删除以today开头的那一行
sed '/^today/d' test.txt
# 删除以today开头的那一行到最后一行之间的所有内容
sed '/^today/,$d' test.txt

d的命令使用起来也比较简单。要注意的是,被删除的行默认是不会输出了,因此默认只会输出那些没被删除的行。

D — 删除多行中的第一行

less test.txt| sed 'N;D'
# 输出
6. ok

D命令比较奇怪。如果模式空间有多行内容的话,它会删除第一行的内容。关键的是,它删除之后还会继续执行命令。

以上面的命令为例子,第一次执行命令时加载第一行,遇上命令N后提前加载第二行,这时候模式空间有两行内容,遇到命令D后第一行的内容被删掉,之后继续执行命令,遇到N,加载第三行,遇到D,删除第二行…这样循环到最后一行的时候,就剩下第6行了。

另外,上面的命令mac上的sed版本不会输出任何内容。

关于模式空间的介绍请看下一节内容。

s — 替换行中的内容

替换命令的使用场景比较多,它的语法如下

[address1[,address2]]s/pattern/replacement/[flags]

pattern表示要匹配的内容,replacement表示要替换的内容,flags表示匹配的一些参数。匹配的内容和替换的内容之间用正斜杠'/'分隔开来,但是我们也可以用其他符号类代替,比如我们要用'#'来分割。

[address1[,address2]]s#pattern#replacement#[flags]

示例:

# 将第一行中的 ',' 替换成 '|' 并输出
sed -n '1s/,/|/p' test.txt
# 输出内容  
1. name|age,sex

上面的我们发现只替换了一个匹配项,如果想把这一行的所有逗号都替换掉,就需要flags参数了。

# 将第一行中的 ',' 全部都替换成 '|' 并输出
sed -n '1s/,/|/gp' test.txt
# 输出内容  
1. name|age|sex

最后的g表示全局替换。

其他的flags介绍

# n —> 只替换第2个逗号,可以换成任意数字n,表示只替换第n个匹配项
sed -n '1s/,/|/2p' test.txt
# w —> 将将替换后的行写入aaa.txt
sed -n '1s#,#|#pw aaa.txt' test.txt
# i -> 匹配的时候忽略大小写.有一些sed版本不支持,比如mac上面的sed就不支持
sed -n '1s/,/|/gip' test.txt

匹配字符串

# 注意,括号前一定要加上转义符号\ 
# 有些版本的sed不支持\+表示匹配1到n个的表达式,另外\w和\d也不支持,比如本人测试时mac就不支持
echo "Three One Two" | sed 's#\(\w\+\) \(\w\+\) \(\w\+\)#\2 \3 \1#'
# 输出
One Two Three

# \d 可以改用 [0-9] 来表示。后面会详细讲sed支持的一些通用正则表达式
# 下面这个语句可以在mac中的sed使用
echo "3,2,1" | sed 's#\([0-9]\),\([0-9]\),\([0-9]\)#\2,\3,\1#'
# 输出
2,3,1

在replacement块中,我们可以用 \n 表示匹配到的第n个匹配内容,然后使用,比如\2就表示第二个匹配到的匹配项。

a — 在行后添加内容

sed '1 a hello world+++++' test.txt
# 输出
1. name,age,sex
hello world+++++
2. hello world
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok

上面的语句在第一行之后插入一行新的内容'hello world+++++'。另外,上面的命令在mac版的sed下也无法执行,会报错。

# 在第三行到最后一行后面都插入语句
sed '3,$ a hello world+++++' test.txt
# 在匹配到class的行到最后一行的后面都插入语句
sed '/class/,$ a hello world+++++' test.txt

i — 在行前添加内容

sed '1 i hello world+++++' test.txt
# 输出
hello world+++++
1. name,age,sex
2. hello world
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok

用法和a命令相似

c — 把整行替换成指定的内容

# 将第一行替换成 hello world+++++
sed '1 c  hello world+++++' test.txt
# 输出
hello world+++++
2. hello world
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok

# 将多行替换成单行
sed '1,3 c  hello world+++++' test.txt

y — 对单个字符进行替换

# 将第1,2行中的所有字符替换成目标字符 
# 比如扫描到h字符的时候,会被转换成1.扫描到e的时候,会被替换成字符2,以此类推
sed '1,2 y/hello/12345/' test.txt
# 输出
1. nam2,ag2,s2x
2. 12335 w5r3d
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok

注意,y命令两边的字符数量必须完全一样,否则会报错。不要把它当做是单词的转换,这是一个一个

n — 对当前行的下一行进行操作

# 将匹配到name那行的下一行中的hello替换成world
sed '/name/n;s/hello/world/' test.txt

n 命令表示对目标行的下一行进行操作,而不是对当前行进行操作。

N — 提前加载下一行

# 提前加载下一行的内容并将两行之间的换行符替换成,号
less test.txt| sed 'N; s/\n/,/'
# 输出
1. name,age,sex,2. hello world
3. today is 10.10,4. my name is jack
5. public class aaa,6. ok

注意,该命令和n不同,n命令是加载下一行覆盖模式空间当前行的内容。而大写N命令是提前加载下一行的内容,然后追加到当前模式空间。并且到时下一行的内容不会再加载。上面命令的效果也就是1,2行一起处理,3,4行一起处理...

关于模式空间的介绍请看下一节内容。

r — 将外部文件读进来

echo 12345 > hello.txt
# 在第2行后面写入hello.txt的值
sed '2 r hello.txt' test.txt

r 命令用来读入外部文件的内容,然后在sed处理行的时候,在行之后插入该内容

w — 将命令处理的内容写入新的文件

# 将第2行的 hello 替换成 world后 写入hello.txt 文件中
sed -n '2s/hello/wolld/w hello.txt' test.txt

! — 对所选行以外的所有行应用命令

# 输出第二行外的所有行
sed -n '2! p' test.txt
# 输出 匹配到hello的行到匹配到class的行 以外 所有行
sed -n '/hello/,/class/! p' test.txt

l — 输出时带隐藏字符

# 输出1到3行,并将隐藏字符显示出来
sed -n '1,3l' test.txt
# 输出
1. name,age,sex$
2. hello world$
3. today is 10.10$

l 可以将隐藏的字符输出出来。比如当你不知道是空格还是制表符时,可以用l来输出。如果是制表符,则会输出\t,如果是换行符,则会输出$来。

q — 退出当前的执行流

# 执行行匹配到hello时就退出,不再往下执行
sed '/hello/ q' test.txt

当执行遇到q命令时,sed会关闭输入流,不再读取后面的行。

& — 输出正则匹配到的内容

& 命令用于输出匹配到的内容,一般和s修改命令一起使用。

# 去匹配 h.*lo,匹配到后用 '匹配项,yes' 来代替原有的内容
# 这里的匹配默认是用最长匹配,比如如果用的表达式是  h.* 。则会匹配h字符到该行的最后一个字符
less test.txt| sed -n 's/h.*lo/&,yes/p'
# 输出,hello 被替换成了  hello,yes
2. hello,yes world

= — 输出行号

= 命令用于在sed执行过程中输出行号

# 输出1-3行的行号
less test.txt | sed '1,3='
# 输出
1
1. name,age,sex
2
2. hello world
3
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok

# 获取文件的总行数
less test.txt| sed -n '$ ='

3. 模式空间和暂存空间

上面一节已经将大部分常用命令介绍完了。还剩下h、H、g、G、x命令没有介绍。在介绍这5个命令前大家需要对sed的模式空间和暂存空间有一定了解。

这两个空间其实就是两个内存缓冲区,sed会将读取的每一行先放到模式空间中,然后再执行相应命令。注意,模式空间并不会保存之前读过的所有行记录。也就是说,当sed读取第二行的记录时,第一行的记录就已经不在模式空间中了。采用的是覆盖的方式

模式空间的内容是不断更新的,但是存在暂存空间里的行内容却会在一直存在(在sed命令执行过程中)。我们可以通过h命令将模式空间的当前行内容复制到暂存空间(覆盖),或者用H命令将模式空间的内容以追加的方式写到暂存空间的末尾。g和G命令就是反着来的,g命令是将暂存空间的内容覆盖到模式空间中,G命令则是追加的方式。最后,x命令是交换两个空间的内容。

# 将第2行的内容复制到暂存空间,第5行的内容追加到暂存空间,最后扫描到最后一行时,将暂存的空间的内容复制回模式空间
# 由于sed会自动输出模式空间的行内容,所以最后会额外再输出第2行和第5行的内容
# 注意,$G 如果使用小写的g,则会造成最后一行被覆盖的情况,也就是最后一行不会被输出来
less test.txt | sed '2h;5H;$G'
# 等同于下面这条语句
less test.txt | sed -e '2h' -e '5H' -e '$G'
# 输出
1. name,age,sex,sss,aaa,www
2. hello world
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok
2. hello world
5. public class aaa

# 遇到包含today的行时,先复制到暂存空间。直到遇到包含class的行时,就把暂存空间的内容取出来替代这一行
sed -e '/today/h' -e '/class/x' test.txt

三、sed 支持的正则表达式

sed各个版本对正则的支持好像不太一样。比如本人用的mac上的sed,有些正则不能匹配,放在centos上执行却可以匹配上。

# 在centos上输出为 10.10 。但是在mac上输出为空
echo "10.10" | sed -n '/[0-9]\+.[0-9]\+/p'

# 下面这个命令在mac和centos上都可以输出
echo "10.10" | sed -n '/[0-9]\{1,\}\.[0-9]\{1,\}/p'

上面的例子可以看出,mac版本的sed对'\+'的无法识别,centos的sed却可以。所以我们最好使用'{1,}'来表示匹配1个到多个。另外,在sed中,如果要用'()'或者'{}'时,记得加上转义符''。如果只是用 '{1,}'则无法表示匹配1个到多个的情况,要写成'\{1,\}'才可以。正常要输出'(){}'符号时才不加转义符。这和我们平常用的正则不太一样

在sed中,像'\d' '\D' '\w' '\W' 最好不要用,因为sed的版本不一定支持。我们可以用[0-9] [^0-9] [a-z] [A-Z]等字符来替代效果。

下面的表格列出了所有版本的sed都支持的正则表达式元字符:

元字符 功能 示例
^ 行首定位符 /^my/ 匹配所有以my开头的行
$ 行尾定位符 /my$/ 匹配所有以my结尾的行
. 匹配除换行符以外的单个字符 /m..y/ 匹配包含字母m,后跟两个任意字符,再跟字母y的行
* 匹配零个或多个前导字符 /my*/ 匹配包含字母m,后跟零个或多个y字母的行
[] 匹配指定字符组内的任一字符 /[Mm]y/ 匹配包含My或my的行
[^] 匹配不在指定字符组内的任一字符 /[^Mm]y/ 匹配包含y,但y之前的那个字符不是M或m的行
.... 保存已匹配的字符 1,20s/youyouself/\1r/ 标记元字符之间的模式,并将其保存为标签1,之后可以使用\1来引用它。最多可以定义9个标签,从左边开始编号,最左边的是第一个。此例中,对第1到第20行进行处理,you被保存为标签1,如果发现youself,则替换为your。
& 保存查找串以便在替换串中引用 s/my/&/ 符号&代表查找串。my将被替换为my
\< 词首定位符 /\
\> 词尾定位符 /my\>/ 匹配包含以my结尾的单词的行
x\{m\} 连续m个x /9\{5\}/匹配包含连续5个9的行
x\{m,\} 至少m个x /9\{5,\}/ 匹配包含至少连续5个9的行
x\{m,n\} 至少m个,但不超过n个x /9\{5,7\}/ 匹配包含连续5到7个9的行

四、分支和跳转

b命令 — 无条件跳转

我们可以通过:label来定义分支,之后通过b命令来跳转到该分支处执行具体的命令。跳转到目标分支后中间的命令会被跳过。

定义分支的语法:

:label;[命令];
# 定义了一个label分支,用于输出行内容。
sed ':label;p'

示例:

# /hello/b Print 表示匹配到hello字段就跳转到 Print分支处。Print分支定义了 p命令,也就是用于输出这一行的内容
# /name/b alter  表示匹配到ok字段就跳转到 alter分支处。alter分支定义了s命令,用于修改内容并输出
# b no 如果前面的两个都没匹配到,就跳转到 no分支去,也就是什么都不做
less test.txt| sed -n '/hello/b Print;/name/b alter;b no;:Print;p;:alter;s/name/123/p;:no;'

# 输出 
1. 123,age,sex
2. hello world
4. my 123 is jack

注意,上面的命令如果没有定义no分支并跳转的话,没匹配到Print分支和alter分支的行会继续往下执行命令,也就是执行 p命令和 's/name/123/p' 命令。

less test.txt| sed -n '/hello/b Print;/name/b alter;:Print;p;:alter;s/name/123/p;'

# 输出
1. 123,age,sex
2. hello world
3. today is 10.10
4. my 123 is jack
5. public class aaa
6. ok

t 命令 — 满足条件才跳转

我们也可以使用t命令来跳转到某个分支。和b命令不同的是,t命令必须在前一个置换(s)命令被执行成功后才跳转。我们将上面的那个b命令换成t命令看一下会输出什么

less test.txt| sed -n '/hello/t Print;/name/t alter;t no;:Print;p;:alter;s/name/123/p;:no;'

# 输出
1. name,age,sex
1. 123,age,sex
2. hello world
3. today is 10.10
4. my name is jack
4. my 123 is jack
5. public class aaa
6. ok

# 上面的命令等同于
less test.txt| sed -n 'p;s/name/123/p'

我们看到完全不同的输出。由于/hello/t Print;/name/t alter;之前没有任何置换命令被执行成功,所以这几个跳转都不会生效,sed会直接往下执行。

t命令的实际应用

# 往包含到 hello所在的行插入 ----- 
less test.txt| sed -n ':Loop;/hello/s/^/-/;/-----/!t Loop;p'
# 输出
1. name,age,sex
-----2. hello world
3. today is 10.10
4. my name is jack
5. public class aaa
6. ok

上面的执行块我们可以分为4个部分看::Loop /hello/s/^/-/ /-----/!t Loop p。第一块定义了一个标签Loop,后面第二块表示如果匹配到hello的行,就往前面添加一个'-',这时说明置换成功,则会触发后面的t命令生效。执行到第三块时,由于前面t命令只有在前面有置换命令成功才会跳转,所以第三块的命令只会对包含hello的行起作用。然后不断重复往行首添加'-',直到该行行首的字符满足'——'。

五、sed 脚本使用

通过 -f scriptfile 来将sed执行语句放到脚本中。我们可以编写一个sed脚本test.awk

/hello/p
/today/p

之后执行

sed -n -f test.sed test.txt

等同于执行

sed -n '/hello/p;/today/p'

awk脚本也可以这么写

#!/bin/sed -f
/hello/p
/today/p

之后直接执行该脚本即可

./test.sed -n test.txt

六、结尾

sed的内容还是很多的,本篇博客也只是简单的介绍了下各个命令的使用以及sed的一些概念。读者朋友们也不要因为这么多的内容而放弃学习sed。sed真的是一款功能强大的编辑器,非常推荐学习。

其实,我们只要对sed的关键语法以及一些常用的命令有所了解就足够了。我们的大多数使用场景可能也就用用s命令、d命令、p命令这些。因此,想快速学习sed的同学建议仔细看完第二节的第一小节,然后挑几个自己感兴趣的命令看就足够了。

你可能感兴趣的:(【linux命令学习】— sed 命令学习)