3.2 字符截取和替换命令
在Linux中,文件的结构是在目录中保存文件或子目录,而在文件中保存字符串。因为Shell编程主要就是和字符串打交道,所以我们在这里介绍字符截取和替换命令。
其实grep也是一个字符串处理命令,它的作用是在文件中提取符合条件的行。但是因为知识点的需要,我们在前面已经讲解了grep命令,不过大家要知道grep命令属于字符串处理命令。
3.2.1 cut列提取命令
grep命令是在文件中提取符合条件的行,也就是分析一行的信息,如果行中包含需要的信息,就把该行提取出来。而如果要进行列提取,就要利用cut命令了。不过要小心,虽然cut命令用于提取符合条件的列,但是也要一行一行地进行数据读取。也就是说,先要读取文本的第一行数据,在此行中判断是否有符合条件的字段,然后再处理第二行数据。我们也可以把cut称为字段提取命令。命令格式如下:
[root@localhost ~]# cut [选项] 文件名 选项: -f列号: 提取第几列 -d分隔符: 按照指定分隔符分割列 -c字符范围: 不依赖分隔符来区分列,而是通过字符范围(行首为0)来进行字段 提取。“n-”表示从第n个字符到行尾;“n-m”表示从第n个字符到第m 个字符;“-m”表示从第1个字符到第m个字符
cut命令的默认分隔符是制表符,也就是Tab键,不过对空格符支持得不怎么好。我们先建立一个测试文件,然后看看cut命令的作用。
[root@localhost ~]# vi student.txt ID Name gender Mark 1 Liming M 86 2 Sc M 90 3 Gao M 83
建立学员成绩表,注意这张表中所有的分隔符都是制表符,不能是空格,否则后面的实验会出现问题。先看看cut命令该如何使用,命令如下:
[root@localhost ~]# cut -f 2 student.txt #提取第二列的内容 Name Liming Sc Gao
如果想要提取多列呢?将列号直接用“, ”隔开,命令如下:
[root@localhost ~]# cut -f 2,3 student.txt #提取第二列和第三列的内容 Name gender Liming M Sc M Gao M
cut命令可以按照字符进行提取。需要注意的是,“8-”代表提取所有行从第8个字符到行尾,而“10-20”代表提取所有行的第10~20个字符,而“-8”代表提取所有行从行首到第8个字符,命令如下:
[root@localhost ~]# cut -c 8- student.txt #提取每行从第8个字符到行尾,好像很乱啊,那是因为每行的字符个数不相等 gender Mark g M 86 90 83
当然,cut命令也可以手工指定分隔符。例如,我想看看当前Linux服务器中有哪些用户、这些用户的UID是什么,就可以这样操作:
[root@localhost ~]# cut -d ":" -f 1,3 /etc/passwd #以“:”作为分隔符,提取/etc/passwd文件的第一列和第三列 root:0 bin:1 daemon:2 adm:3 lp:4
cut命令很方便,不过最主要的问题是对空格识别得不好,很多命令的输出格式中都不是制表符,而是空格符,比如:
[root@localhost ~]# df #统计分区使用状况 文件系统 1K-块 已用 可用 已用% 挂载点 /dev/sda3 19923216 1848936 17062212 10% / tmpfs 312672 0 312672 0% /dev/shm /dev/sda1 198337 26359 161738 15% /boot /dev/sr0 3626176 3626176 0 100% /mnt/cdrom
如果想用cut命令截取第一列和第三列,就会出现这样的情况:
[root@localhost ~]# df -h | cut -d " " -f 1,3 文件系统 /dev/sda3 tmpfs /dev/sda1 /dev/sr0
第三列去哪里了啊?其实因为df命令输出的分隔符不是制表符,而是多个空格符,所以cut命令会忠实地将每个空格符当作一个分隔符,而这样数,第三列刚好也是空格,所以输出才会是上面这种情况。总之,cut命令不能很好地识别空格符。如果想要以空格符作为分隔符,就要使用awk命令了。
3.2.2 awk编程
1.概述
本小节我们要讲awk编程,光看标题就觉得好大啊,awk已经不能被看成一条命令了,而是一种语言。awk编程用于在Linux/UNIX下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或者其他命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是Linux/Unix下的一个强大的编程工具。它在命令行中使用,但更多的是作为脚本来使用的。awk处理文本和数据的方式是这样的:它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕);如果没有指定模式,则所有被操作所指定的行都将被处理。awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了贝尔实验室和GNU的一些扩展。下面介绍的awk是以GUN的gawk为例的,在Linux系统中已把awk链接到gawk为例,所以下面全部以gawk为例进行介绍。
awk有许多用途,包括从文件中提取数据、统计在文件内出现的次数及生成报告。由于awk的基本语法与C语言的相似,所以,假如你已经熟悉了C语言,就会了解awk的大部分用法。在许多方面,可以说awk是C语言的一种简易版本。如果你还不熟悉C语言,那么学习awk要比学习C语言容易一些。awk对于Linux环境是易适应的,awk含有预定义的变量,可自动实现许多编程任务,提供常规变量,支持C格式化输出。awk可以把Shell脚本和C编程的精华结合在一起。在awk内执行同一任务通常有许多不同的方法,应判断哪种方法最适合应用。awk会自动读取每个记录,把记录分成字段,并在需要时进行类型转换。变量使用的方式确定了它的类型,用户不必声明变量的任何类型。
2.printf格式化输出
printf是awk的重要格式化输出命令,我们需要先介绍一下printf命令如何使用。需要注意,在awk中可以识别print输出动作和printf输出动作(区别是:print会在每个输出之后自动加入一个换行符;而printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,则需要手工加入换行符),但是在Bash中只能识别标准格式化输出命令printf。所以我们在本小节中介绍的是标准格式化输出命令printf。命令格式如下:
[root@localhost ~]# printf ’输出类型输出格式’ 输出内容 输出类型: %ns: 输出字符串。n是数字,指代输出几个字符 %ni: 输出整数。n是数字,指代输出几个数字 %m.nf: 输出浮点数。m和n是数字,指代输出的整数位数和小数位数。如%8.2f 代表共输出8位数,其中2位是小数,6位是整数 输出格式: \a: 输出警告声音 \b: 输出退格键,也就是Backspace键 \f: 清除屏幕 \n: 换行 \r: 回车,也就是Enter键 \t: 水平输出退格键,也就是Tab键 \v: 垂直输出退格键,也就是Tab键
为了演示printf命令,我们需要修改一下刚刚cut命令使用的student.txt文件。文件内容如下:
[root@localhost ~]# vi student.txt ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
我们使用printf命令输出这个文件的内容,如下:
[root@localhost ~]# printf '%s' $(cat student.txt) IDNamegenderPHPLinuxMySQLAverage1LimingM82958687.662ScM74968785.663GaoM998393 91.66[root@localhost ~]#
输出结果十分混乱。这就是printf命令,如果不指定输出格式,则会把所有输出内容连在一起输出。其实文本的输出本身就是这样的,cat等文本输出命令之所以可以按照格式漂亮地输出,那是因为cat命令已经设定了输出格式。那么,为了用printf输出合理的格式,应该这样做:
[root@localhost ~]# printf '%s\t %s\t %s\t %s\t %s\t %s\t \n' $(cat student.txt) #注意:在printf命令的单引号中只能识别格式输出符号,而手工输入的空格是无效的 ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
再强调一下:在printf命令的单引号中输入的任何空格都不会反映到格式输出中,只有格式输出符号才能影响printf命令的输出结果。
解释一下这个命令:因为我们的文档有6列,所以使用6个“%s”代表这6列字符串,每个字符串之间用“\t”分隔;最后还要加入“\n”,使得每行输出都换行,否则这些数据还是会连成一行的。
如果不想把成绩当成字符串输出,而是按照整型和浮点型输出,则要这样做:
[root@localhost ~]# printf '%i\t %s\t %i\t %i\t %i\t %8.2f\t \n' \ $(cat student.txt | grep -v Name) 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
先解释“cat student.txt | grep -v Name”这条命令。这条命令会把第一行标题取消,剩余的内容才用printf格式化输出。在剩余的内容中,第1、3、4、5列为整型,所以用“%i”输出;而第2列是字符串,所以用“%s”输出;而第6列是小数,所以用“%8.2f”输出。“%8.2f”代表可以输出8位数,其中有2位是小数,有6位是整数。
printf命令是awk中重要的输出动作,不过awk中也能识别print动作,区别刚刚已经介绍了,当然稍后我们还会举例来说明一下这两个动作的区别。注意:在Bash中只有printf命令。另外,printf命令只能格式化输出具体数据,不能直接输出文件内容或使用管道符,所以printf命令的格式还是比较特殊的。
3.awk基本使用
awk命令的基本格式如下:
[root@localhost ~]# awk ’条件1{动作1} 条件2{动作2}…' 文件名 条件(Pattern): 一般使用关系表达式作为条件。这些关系表达式非常多,具体参考表3-3。例如: x > 10 判断变量x是否大于10 x == y 判断变量x是否等于变量y A ~ B 判断字符串A中是否包含能匹配B表达式的子字符串 A ! ~ B 判断字符串A中是否不包含能匹配B表达式的子字符串 动作(Action): 格式化输出 流程控制语句
我们先来学习awk的基本用法,也就是只看看格式化输出动作是干什么的。至于条件类型和流程控制语句,我们在后面再详细介绍。看看这个例子:
表3-3 awk支持的主要条件类型
[root@localhost ~]# awk '{printf $2 "\t" $6 "\n"}' student.txt #输出第二列和第六列的内容 Name Average Liming 87.66 Sc 85.66 Gao 91.66
在这个例子中没有设定任何的条件类型,所以这个文件中的所有内容都符合条件,动作会无条件执行。动作是格式化输出printf,“$2”和“$6”分别代表第二个字段和第六个字段,所以这条awk命令会列出student.tx文件的第二个字段和第六个字段。本来printf命令中定义输出格式应该使用单引号,可以单引号被awk命令固定使用了,所以只能使用双引号。
虽然都是截取列的命令,但是awk命令比cut命令智能多了,cut命令是不能很好地识别空格作为分隔符的;而对于awk命令来说,只要分隔开,不管是空格还是制表符,都可以识别。比如刚刚截取df命令的结果时,cut命令已经力不从心了,我们来看看awk命令,命令如下:
[root@localhost ~]# df -h | awk '{print $1 "\t" $3}' 文件系统 已用 /dev/sda3 1.8G tmpfs 0 /dev/sda1 26M /dev/sr0 3.5G
在这两个例子中,我们分别使用了printf动作和print动作。发现了吗?如果使用printf动作,就必须在最后加入“\n”,因为printf只能识别标准输出格式;如果我们不使用“\n”,它就不会换行。而print动作则会在每次输出后自动换行,所以不用在最后加入“\n”。
4.awk的条件
我们来看看awk可以支持什么样的条件类型吧。awk支持的主要条件类型如表3-3所示。
1)BEGIN
BEGIN是awk的保留字,是一种特殊的条件类型。BEGIN的执行时机是“在awk程序一开始,尚未读取任何数据之前”。一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据时,BEGIN的条件就不再成立,所以BEGIN定义的动作只能被执行一次。例如:
[root@localhost ~]# awk 'BEGIN{printf "This is a transcript \n" } {printf $2 "\t" $6 "\n"}' student.txt #awk命令只要检测不到完整的单引号就不会执行,所以这条命令的换行不用加入“\”,就是一行命令 #这里定义了两个动作 #第一个动作使用BEGIN条件,所以会在读入文件数据前打印“这是一张成绩单”(只会执行一次) #第二个动作会打印文件的第二个字段和第六个字段 This is a transcript Name Average Liming 87.66 Sc 85.66 Gao 91.66
2)END
END也是awk的保留字,不过刚好和BEGIN相反。END是在awk程序处理完所有数据,即将结束时执行的。END后的动作只在程序结束时执行一次。例如:
[root@localhost ~]# awk 'END{printf "The End \n" }
{printf $2 "\t" $6 "\n"}' student.txt
#在输出结尾输入“The End”,这并不是文档本身的内容,而且只会执行一次
Name Average
Liming 87.66
Sc 85.66
Gao 91.66
The End
3)关系运算符
举几个例子来看看关系运算符。假设我想看看平均成绩大于等于87分的学员是谁,就可以这样输入命令:
例子1: [root@localhost ~]# cat student.txt | grep -v Name | \ awk '$6 >= 87 {printf $2 "\n" }' #使用cat输出文件内容,用grep取反包含“Name”的行 #判断第六个字段(平均成绩)大于等于87分的行,如果判断式成立,则打印第六列(学员名) Liming Gao
在加入了条件之后,只有条件成立,动作才会执行;如果条件不满足,则动作不执行。通过这个实验,大家可以发现,虽然awk是列提取命令,但是也要按行来读入。这条命令的执行过程是这样的:
(1)如果有BEGIN条件,则先执行BEGIN定义的动作。
(2)如果没有BEGIN条件,则读入第一行,把第一行的数据依次赋予$0、$1、$2等变量。其中,$0代表此行的整体数据,$1代表第一个字段,$2代表第二个字段。
(3)依据条件类型判断动作是否执行。如果条件符合,则执行动作;否则读入下一行数据。如果没有条件,则每行都执行动作。
(4)读入下一行数据,重复执行以上步骤。
如果我想看看Sc用户的平均成绩呢?
例子2: [root@localhost ~]# awk '$2~ /Sc/ {printf $6 "\n"}' student.txt #如果第二个字段中包含“Sc”字符,则打印第六个字段 85.66
这里要注意,在awk中,只有使用“//”包含的字符串,awk命令才会查找。也就是说,字符串必须用“//”包含,awk命令才能正确识别。
4)正则表达式
如果想让awk识别字符串,则必须使用“//”包含,例如:
[root@localhost ~]# awk '/Liming/ {print}' student.txt #打印Liming的成绩 1 Liming 82 95 86 87.66
当使用df命令查看分区的使用情况时,如果我只想查看真正的系统分区的使用情况,而不想查看光盘和临时分区的使用情况,则可以这样做:
[root@localhost ~]# df -h | awk '/sda[0-9]/ {printf $1 "\t" $5 "\n"} ' #查询包含“sda数字”的行,并打印第一个字段和第五个字段 /dev/sda3 10% /dev/sda1 15%
5.awk内置变量
我们已经知道了,在awk中,“$1”代表第一个字段(列),“$2”代表第二个字段。而“$n”(n为数字)就是awk的内置变量,下面介绍一下awk中常见的内置变量,如表3-4所示。
表3-4 awk中常见的内置变量
刚刚我们使用awk命令都是使用任意空格(制表符或空格)作为分隔符的,如果我们要截取的数据不是用空格作为分隔符的,那又该怎么办呢?这时“FS”内置变量就该出场了,命令如下:
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | \ awk '{FS=":"} {printf $1 "\t" $3 "\n"}' #查询可以登录的用户的用户名和UID root:x:0:0:root:/root:/bin/bash user1 501
这里“:”分隔符生效了,但是对第一行却没有起作用,原来我们忘记了“BEGIN”条件。再来试试:
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | \ awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\n"}' root 0 user1 501
这次的输出就没有任何问题了。
再来看看内置变量“NF”和“NR”是用来干什么的,命令如下:
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | \
awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\t行号:" NR "\t字段数:" NF "\n"}'
#解释一下awk命令
#开始执行{分隔符是“:”} {输出第一个字段和第三个字段输出行号(NR值)字段数(NF值)}
root 0 行号:1 字段数:7
user1 501 行号:2 字段数:7
有点奇怪,root行确实是第一行,而user1行应该是/etc/passwd文件的最后一行,怎么能是第二行呢?那是因为grep命令把所有的伪用户都过滤了,传入awk命令的只有两行数据。这里为了便于理解,在输出的说明中使用了中文。如果想支持中文,则Linux必须安装中文字体,当然远程工具也要支持中文(Linux本机的纯字符界面是不能输入中文的)。如果你在做实验时不能输入中文,那只要明白是什么意思,换成英文也可以。
如果我只想看看sshd这个伪用户的相关信息,则可以这样使用:
[root@localhost ~]# cat /etc/passwd | \ awk'BEGIN{FS=":"}$1=="sshd"{printf$1"\t"$3"\t行号:"NR"\t字 段数:"NF"\n"}' #可以看到sshd伪用户的UID是74,是/etc/passwd文件的第28行,此行有7个字段 sshd 74 行号:28 字段数:7
6.awk流程控制
之所以称为awk编程,是因为在awk中允许定义变量,允许使用运算符,允许使用流程控制语句和定义函数。这样就使得awk编程成了一门完整的程序语言,当然难度也比普通的命令要大得多。所有语言的流程控制都非常类似,稍后我们会详细地讲解Bash的流程控制。在这里只举一些例子,用来演示awk流程控制的作用。如果你现在看不懂这些例子,则可以等学习完Bash流程控制之后,回过头来学习。
我们再利用student.txt文件做一个练习,后面的使用比较复杂,我们再看看这个文件的内容,如下:
[root@localhost ~]# cat student.txt ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
先来看看如何在awk中定义变量与调用变量的值。假设我想统计PHP成绩的总分,就应该这样做:
[root@localhost ~]# awk 'NR==2{php1=$3} NR==3{php2=$3} NR==4{php3=$3; totle=php1+php2+php3; print "totle php is " totle}' student.txt #统计PHP成绩的总分 totle php is 255
这条命令有点复杂了,我们解释一下。“NR==2{php1=$3}”(条件是NR==2,动作是php1=$3)是指如果输入数据是第二行(第一行是标题行),就把第二行的第三个字段的值赋予变量“php1”。“NR==3{php2=$3}”是指如果输入数据是第三行,就把第三行的第三个字段的值赋予变量“php2”。“NR==4{php3=$3; totle=php1+php2+php3; print "totle php is " totle}”(“NR==4”是条件,后面{}中的都是动作)是指如果输入数据是第四行,就把第四行的第三个字段的值赋予变量“php3”;然后定义变量totle的值是“php1+php2+php3”;最后输出“totle php is”关键字,后面加变量totle的值。
在awk编程中,因为命令语句非常长,所以在输入格式时需要注意以下内容:
· 多个条件{动作}可以用空格分隔,也可以用回车分隔。
· 在一个动作中,如果需要执行多条命令,则需要用“; ”分隔,或用回车分隔。
· 在awk中,变量的赋值与调用都不需要加入“$”符号。
· 在条件中判断两个值是否相同,请使用“==”,以便和变量赋值进行区分。
再看看如何实现流程控制。假设Linux成绩大于90分,就是一个好男人,命令如下:
[root@localhost ~]# awk '{if (NR>=2) {if ($4>90) printf $2 " is a good man! \n"}}' student.txt #程序中有两个if判断,第一个判断行号大于2,第二个判断Linux成绩大于90分 Liming is a good man! Sc is a good man!
其实在awk中,if判断语句完全可以直接利用awk自带的条件来取代,刚刚的脚本可以改写成这样:
[root@localhost ~]# awk ' NR>=2 {test=$4} test>90 {printf $2 " is a good man! \n"}' student.txt #先判断行号,如果大于2,就把第四个字段的值赋予变量test #再判断成绩,如果test的值大于90分,就打印好男人 Liming is a good man! Sc is a good man!
7.awk函数
awk编程也允许在编程时使用函数,下面我们讲讲awk的自定义函数。awk函数的定义方法如下:
function函数名(参数列表){ 函数体 }
我们定义一个简单的函数,使用该函数来打印student.txt的学员姓名和平均成绩。命令如下:
[root@localhost ~]# awk 'function test(a, b) { printf a "\t" b "\n" } #定义函数test,包含两个参数,函数体的内容是输出这两个参数的值 { test($2, $6) } ' student.txt #调用函数test,并向两个参数传递值 Name Average Liming 87.66 Sc 85.66 Gao 91.66
8.awk中的脚本调用
对于小的单行程序来说,将脚本作为命令行自变量传递给awk是非常简单的;而对于多行程序来说,就比较难处理了。当程序是多行的时候,使用外部脚本是很适合的。首先在外部文件中写好脚本,然后可以使用awk命令的-f选项,使其读入脚本并且执行。
例如,我们可以先编写一个awk脚本。
[root@localhost ~]# vi pass.awk BEGIN {FS=":"} { print $1 "\t" $3}
然后使用“-f”选项来调用这个脚本。
[root@localhost ~]# awk -f pass.awk /etc/passwd root 0 bin 1 daemon 2 …省略部分输出…
如果是一些较为复杂的awk语句,而且需要重复调用,那么把它放入脚本文件中是最为经济和方便的方法了。
3.2.3 sed命令
sed是一种几乎可以应用在所有UNIX平台(包括Linux)上的轻量级流编辑器。sed有许多很好的特性。首先,它相当小巧,通常要比你所喜爱的脚本语言小很多倍。其次,因为sed是一种流编辑器,所以,它可以对从如管道这样的标准输入中接收的数据进行编辑。因此,无须将要编辑的数据存储在磁盘上的文件中。因为可以轻易将数据管道输出到sed,所以,将sed用作强大的Shell脚本中长而复杂的管道很容易。
sed主要是用来将数据进行选取、替换、删除、新增的命令。我们看看命令的语法:
[root@localhost ~]# sed [选项] '[动作]' 文件名 选项: -n: 一般sed命令会把所有数据都输出到屏幕上。如果加入此选项,则只会 把经过sed命令处理的行输出到屏幕上 -e: 允许对输入数据应用多条sed命令编辑 -f脚本文件名: 从sed脚本中读入sed操作。和awk命令的-f选项非常类似 -r: 在sed中支持扩展正则表达式 -i: 用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出 动作: a \: 追加,在当前行后添加一行或多行。当添加多行时,除最后一行外, 每行末尾需要用“\”代表数据未完结 c \: 行替换,用c后面的字符串替换原数据行。当替换多行时,除最后一行 外,每行末尾需用“\”代表数据未完结 i \: 插入,在当前行前插入一行或多行。当插入多行时,除最后一行外, 每行末尾需要用“\”代表数据未完结 d: 删除,删除指定的行 p: 打印,输出指定的行 s: 字符串替换,用一个字符串替换另一个字符串。格式为“行范围s/ 旧字串/新字串/g”(和Vim中的替换格式类似)
大家需要注意,sed所做的修改并不会直接改变文件的内容(如果是用管道符接收的命令的输出,则连文件都没有),而是把修改结果只显示到屏幕上,除非使用“-i”选项才会直接修改文件。
1.行数据操作
闲话少叙,直奔主题,我们举几个例子来看看sed命令到底是干什么的。假设我想查看一下student.txt文件的第二行,就可以利用“p”动作了。
[root@localhost ~]# sed '2p' student.txt ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
好像看着不怎么顺眼啊!“p”动作确实输出了第二行数据,但是sed命令还会把所有数据都输出一次,这时就会看到这个比较奇怪的结果。那如果我想指定输出某行数据,就需要“-n”选项的帮助了。
[root@localhost ~]# sed -n '2p' student.txt 1 Liming 82 95 86 87.66
这样才可以输出指定的行。大家可以这样记忆:当我们需要输出指定的行时,需要把“-n”选项和“p”动作一起使用。
再来看看如何删除文件中的数据。
[root@localhost ~]# sed '2,4d' student.txt #删除从第二行到第四行的数据 ID Name PHP Linux MySQL Average [root@localhost ~]# cat student.txt #文件本身并没有被修改 ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
看到这条命令首先需要注意,所有的动作必须使用“单引号”包含;其次,在动作中可以使用数字代表行号,逗号代表连续的行范围。还可以使用“$”代表最后一行,如果动作是“2, $d”,则代表从第二行删除到最后一行。
再来看看如何追加和插入行数据。
[root@localhost ~]# sed '2a hello' student.txt #在第二行后加入hello ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 hello 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
“a”动作会在指定行后追加数据。如果想要在指定行前插入数据,则需要使用“i”动作。
[root@localhost ~]# sed '2i hello \ > world' student.txt #在第二行前插入两行数据 ID Name PHP Linux MySQL Average hello world 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
如果想追加或插入多行数据,那么,除最后一行外,每行的末尾都要加入“\”代表数据未完结。
再来看看“-n”选项的作用,命令如下:
[root@localhost ~]# sed -n '2i hello \ #只查看sed命令操作的数据 world' student.txt hello world
看到了吧,“-n”只用于查看sed命令操作的数据,而并非查看所有的数据。
再来看看如何实现行数据替换。假设李明老师的成绩太好了,我实在是不想看到他的成绩刺激我,那我就可以这样做:
[root@localhost ~]# cat student.txt | sed '2c No such person' ID Name PHP Linux MySQL Average No such person 2 Sc 74 96 87 85.66 3 Gao 99 83 93 91.66
哈哈,第二行数据变成了“查无此人”,看着心情马上就好了起来。通过这个例子我们看到了,sed也可以接收和处理管道符传输的数据。
sed命令在默认情况是不会修改文件内容的。如果我确定需要让sed命令直接处理文件的内容,则可以使用“-i”选项。不过要小心,这样非常容易误操作,在操作系统文件时请小心谨慎。可以使用这样的命令:
[root@localhost ~]# sed -i '2c No such person' student.txt
2.字符串替换
“c”动作是进行整行替换的,如果仅仅想替换行中的部分数据,就要使用“s”动作了。“s”动作的格式如下:
[root@localhost ~]# sed 's/旧字符串/新字符串/g' 文件名
替换的格式和Vim非常类似。假设我觉得自己的PHP成绩太低了,想作弊改高一点,就可以这样做:
[root@localhost ~]# sed '3s/74/99/g' student.txt #在第三行中,把74换成99 ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 2 Sc 99 96 87 85.66 3 Gao 99 83 93 91.66
这样看起来就比较舒服了吧。如果我想把高老师的成绩注释掉,让它不再生效,则可以这样做:
[root@localhost ~]# sed '4s/^/#/g' student.txt #这里使用正则表达式,“^”代表行首 ID Name PHP Linux MySQL Average 1 Liming 82 95 86 87.66 2 Sc 74 96 87 85.66 #3 Gao 99 83 93 91.66
在sed中只能指定行范围,但是很遗憾,我在李明和高老师的中间,不能只把他们两个注释掉,那么我们可以这样做:
[root@localhost ~]# sed -e 's/Liming//g ; s/Gao//g' student.txt #同时把“Liming”和“Gao”替换为空 ID Name PHP Linux MySQL Average 1 82 95 86 87.66 2 Sc 74 96 87 85.66 3 99 83 93 91.66
“-e”选项可以同时执行多个sed动作,当然,如果只执行一个动作,则也可以使用“-e”选项,但是这时没有什么意义。还要注意,多个动作之间要用“; ”或回车分隔,例如,上一条命令也可以这样写:
[root@localhost ~]# sed -e 's/Liming//g > s/Gao//g' student.txt ID Name PHP Linux MySQL Average 1 82 95 86 87.66 2 Sc 74 96 87 85.66 3 99 83 93 91.66
好了,李明老师和高老师的成绩被我折腾够了,关于sed命令我们就讲到这里吧。