之前,写过一篇grep正则表达式的文章。这篇记录下与grep命令具有类似相同功能的两个命令,sed和awk。grep是文本搜索工具,sed是非交互式流编辑器,awk是一种文本格式化工具。
sed是一个非交互式的流编辑器。所谓非交互式,是指使用sed只能在命令行下输入编辑命令来编辑文本,然后在屏幕上查看输出;而所谓流编辑器,是指sed每次只从文件(或输入)读入一行,然后对该行进行指定的处理,并将结果输出到屏幕(除非取消了屏幕输出又没有显式地使用打印命令),接着读入下一行。整个文件像流水一样被逐行处理然后逐行输出。
下面我们看一下sed的工作过程。
sed不是在原输入上直接进行处理的,而是先将读入的行放到缓冲区中,对缓冲区里的内容进行处理,处理完毕后也不会写回原文件(除非用shell的输出重定向来保存结果),而是直接输出到屏幕上。sed运行过程中维护着两个缓冲区,一个是活动的“模式空间(pattern space)”,另一个是起辅助作用的“暂存缓冲区(holding space)”。一般情况下,每当运行sed,sed首先把第一行装入模式空间,进行处理后输出到屏幕,然后将第二行装入模式空间替换掉模式空间里原来的内容,然后进行处理,以此类推。
如图:
sed命令格式
sed [OPTION]... {script-only-if-no-other-script} [input-file]...
OPTION:
-r: 支持扩展正则表达式; -n: 静默模式; -e script1 -e script2 -e script3:指定多脚本运行; -f /path/to/script_file:从指定的文件中读取脚本并运行; -i: 直接修改源文件;
地址定界:
#: 指定行; $: 最后一行; /regexp/:任何能够被regexp所匹配到的行; \%regexp%:同上,只不过换作%为regexp边界符; startline,endline==#,#:从startline到endline #,/regexp/:从#行开始,到第一次被/regexp/所匹配到的行结束,中间的所有行; /regexp1/,/regexp2/:从第一次被/regexp1/匹配到的行开始,到第一次被/regexp2/匹配到的行结束,中间的所有行; #,+n:从#行开始,一直到向下的n行; first~step:指定起始行,以及步长;
sed的编辑命令
d: 删除模式空间中的行; =:显示行号; a\text:附加text i\text:插入text,支持\n实现多行插入; c\text:用text替换匹配到的行; p: 打印模式空间中的行; s/regexp/replacement/:替换由regexp所匹配到的内容为replacement;g: 全局替换; w /path/to/somefile:把指定的内容另存至/path/to/somefile路径所指定的文件中; r /path/from/somefile:在文件的指定位置插入另一个文件的所有内容,完成文件合并;
例子:
一、OPTIONS
1、sed默认使用参数-e,如果不指定“定界位置符”默认读取文件所有行,不指定对文件的操作命令,默认为打印p。
[[email protected] bash]# [[email protected] bash]# sed ‘‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then poor=$[$num1-$num2] num1=$poor elif [ $num2 -gt $num1 ];then poor=$[$num2-$num1] num2=$poor fi done echo $poor [[email protected] bash]# sed -e ‘‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then poor=$[$num1-$num2] num1=$poor elif [ $num2 -gt $num1 ];then poor=$[$num2-$num1] num2=$poor fi done echo $poor [[email protected] bash]#
2、-n选项,必须与地址定界一起使用,否则不打印任何信息,作用是取消默认对文件所有行的操作。
[[email protected] bash]# sed -n ‘‘ a.sh [[email protected] bash]# sed -n ‘1p‘ a.sh #!/bin/bash [[email protected] bash]# sed ‘1p‘ a.sh #!/bin/bash #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then poor=$[$num1-$num2] num1=$poor elif [ $num2 -gt $num1 ];then poor=$[$num2-$num1] num2=$poor fi done echo $poor [[email protected] bash]#
3、-r选项,使用扩展的正则表达式
[[email protected] bash]# sed -n ‘/1|2/p‘ a.sh [[email protected] bash]# [[email protected] bash]# [[email protected] bash]# sed -rn ‘/1|2/p‘ a.sh read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then poor=$[$num1-$num2] num1=$poor elif [ $num2 -gt $num1 ];then poor=$[$num2-$num1] num2=$poor [[email protected] bash]#
4、-e选项,可以对一个文件执行多次sed命令,-e可以使用;代替
[[email protected] bash]# sed -ne ‘1p;2p;4p‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do [[email protected] bash]# [[email protected] bash]# [[email protected] bash]# sed -ne ‘1p‘ -e ‘2p‘ -e ‘4p‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do [[email protected] bash]#
5、-f选项,指定文件,使sed按照文件的定义执行
[[email protected] bash]# cat sed 1p 2p 4p [[email protected] bash]# sed -n -f sed a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do [[email protected] bash]#
6、-i选项,直接修改源文件
[[email protected] bash]# sed -i ‘1p‘ a.sh [[email protected] bash]# cat a.sh #!/bin/bash #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] let num1=$poor elif [ $num2 -gt $num1 ];then let poor=$[$num2-$num1] let num2=$poor fi done echo $poor [[email protected] bash]#
二、地址定界
1、# 数字,用来指定文件第#行,上面的例子都是此种模式。不再举例
2、$ 匹配最后一行
[[email protected] bash]# sed -n ‘$p‘ a.sh echo $poor [[email protected] bash]#
3、/regexp/:任何能够被regexp所匹配到的行,可以使用正则表达式
例如上面-r所举的例子,匹配1或2的行
4、\%regexp%:同上,只不过换作%为regexp边界符;
[[email protected] bash]# sed -nr ‘\%1|2%p‘ a.sh read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] let num1=$poor elif [ $num2 -gt $num1 ];then let poor=$[$num2-$num1] let num2=$poor [[email protected] bash]#
5、startline,endline==#,#:从startline到endline
[[email protected] bash]# sed -n ‘1,4p‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do [[email protected] bash]#
6、#,/regexp/:从#行开始,到第一次被/regexp/所匹配到的行结束,中间的所有行;
[[email protected] bash]# sed -n ‘1,/let/p‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] [[email protected] bash]#
7、/regexp1/,/regexp2/:从第一次被/regexp1/匹配到的行开始,到第一次被/regexp2/匹配到的行结束,中间的所有行;
[[email protected] bash]# sed -n ‘/#/,/let/p‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] [[email protected] bash]#
8、#,+n:从#行开始,一直到向下的n行
[[email protected] bash]# sed -n ‘1,+2p‘ a.sh #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 [[email protected] bash]#
9、first~step:指定起始行,以及步长
[[email protected] bash]# sed -n ‘1~2p‘ a.sh #!/bin/bash if [ $num1 -gt $num2 ];then let num1=$poor let poor=$[$num2-$num1] fi echo $poor [[email protected] bash]#
三、编辑命令
1、d 删除模式空间中的行;
[[email protected] bash]# sed -r ‘/1|2/d‘ a.sh #!/bin/bash fi done echo $poor [[email protected] bash]#
2、= 显示行号;
[[email protected] bash]# sed ‘/1/=‘ a.sh #!/bin/bash 2 read -p "plz enter two integer:" -t 20 num1 num2 4 while [ $num1 != $num2 ];do 5 if [ $num1 -gt $num2 ];then 6 let poor=$[$num1-$num2] 7 let num1=$poor 8 elif [ $num2 -gt $num1 ];then 9 let poor=$[$num2-$num1] let num2=$poor fi done echo $poor [[email protected] bash]#
3、a \text:附加text,在匹配行后添加text
[[email protected] bash]# sed ‘/#/a\hello world!‘ a.sh #!/bin/bash hello world! read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] let num1=$poor elif [ $num2 -gt $num1 ];then let poor=$[$num2-$num1] let num2=$poor fi done echo $poor [[email protected] bash]#
4、i \text:插入text,支持\n实现多行插入,在匹配的行前插入text
[[email protected] bash]# sed ‘/#/i\hello\nworld!‘ a.sh hello world! #!/bin/bash read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] let num1=$poor elif [ $num2 -gt $num1 ];then let poor=$[$num2-$num1] let num2=$poor fi done echo $poor [[email protected] bash]# [[email protected] bash]# [[email protected] bash]# sed -n ‘/#/i\hello\nworld!‘ a.sh hello world! [[email protected] bash]#
5、c \text:用text替换匹配到的行;
[[email protected] bash]# sed ‘/#/c\hello world!‘ a.sh hello world! read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] let num1=$poor elif [ $num2 -gt $num1 ];then let poor=$[$num2-$num1] let num2=$poor fi done echo $poor [[email protected] bash]#
6、p: 打印模式空间中的行;
前面的例子很多,不再列举。
7、s/regexp/replacement/:替换由regexp所匹配到的内容为replacement。g: 全局替换;
[[email protected] bash]# sed -n ‘2p‘ a.sh 123454321 [[email protected] bash]# sed -n ‘2 s/1/A/p‘ a.sh A23454321 [[email protected] bash]# sed -n ‘2 s/1/A/gp‘ a.sh A2345432A
如果没有地址定界,则每行匹配要替换的字符,且只替换每行匹配到的第一个。
[[email protected] bash]#sed -n ‘s/1/A/p‘ a.sh A23454321 read -p "plz enter two integer:" -t 20 numA num2 while [ $numA != $num2 ];do if [ $numA -gt $num2 ];then let poor=$[$numA-$num2] let numA=$poor elif [ $num2 -gt $numA ];then let poor=$[$num2-$numA] [[email protected] bash]#
8、w /path/to/somefile:把指定的内容另存至/path/to/somefile路径所指定的文件中;
[[email protected] bash]# cat sed [[email protected] bash]# sed -n ‘/#/w/root/Desktop/bash/sed‘ a.sh [[email protected] bash]# cat sed #!/bin/bash [[email protected] bash]#
9、r /path/from/somefile:在文件的指定位置插入另一个文件的所有内容,完成文件合并;
[[email protected] bash]# sed ‘$ r/root/Desktop/bash/sed‘ a.sh #!/bin/bash 123454321 read -p "plz enter two integer:" -t 20 num1 num2 while [ $num1 != $num2 ];do if [ $num1 -gt $num2 ];then let poor=$[$num1-$num2] let num1=$poor elif [ $num2 -gt $num1 ];then let poor=$[$num2-$num1] let num2=$poor fi done echo $poor #!/bin/bash hello world #welcome here
sed的高级用法
sed的高级用法需要用到sed的另外一个“保持空间”。前面基本用法中也有提到模式空间,即为处理文件中一行内容的一个临时缓冲区。处理完一行之后就会把模式空间中的内容打印到标准输出,然后自动清空缓存。
而这里说的保持空间是sed中的另外一个缓冲区,此缓冲区正如其名,不会自动清空,但也不会主动把此缓冲区中的内容打印到标准输出中。而是需要以下sed命令进行处理:
h:用模式空间中的内容覆盖保持空间的内容;
H:把模式空间中的内容追加至保持空间中内容的后面;
g:从保持空间中取到其内容,并将其覆盖模式空间中的内容;
G:从保持空间中取到其内容,并将其追加在模式空间中的内容的后面;
x:把保持空间和模式空间中的进行交换;
n:读取匹配到的行的下一行至模式空间;(会覆盖模式空间中的原有内容);
N:读取匹配到的行的下一行至模式空间,追加在模式空间中原有内容的后面;
d:删除模式空间中的内容;
D:删除多行模式空间中的首行;
注意:命令功能可使用!取反;分号可用于分隔脚本;
举例:
1、倒序显示文件内容
[[email protected] bash]# sed ‘1!G;h;$!d‘ sed f e d c b a #!/bin/bash [[email protected] bash]# cat sed #!/bin/bash a b c d e f [[email protected] bash]#
执行过程解析:
命令 sed ‘1!G;h;$!d’ sed 1、读入第一行#!/bin/bash,匹配1!G,不执行G;h命令没有定界,执行h,从模式空间覆盖到保持空间;不匹配$!d,删除模式空间的内容 2、读入第二行a,不匹配1!G,执行G,从保持空间追加到模式空间,变成:a\n#!/bin/bash;执行h,从模式空间覆盖到保持空间,保持空间由#!/bin/bash变成:a\n#!/bin/bash;不匹配$!d,删除模式空间的内容; 3、读入第三行b,匹配模式与第二行相同,保持空间内容变为:b\na\n#!/bin/bash 4、读入第四行c,匹配模式与第二行相同,保持空间内容变为:c\nb\na\n#!/bin/bash 5、读入第五行d,匹配模式与第二行相同,保持空间内容变为:d\nc\nb\na\n#!/bin/bash 6、读入第六行e,匹配模式与第二行相同,保持空间内容变为:e\nd\nc\nb\na\n#!/bin/bash 7、读入第七行f,匹配模式与第二行相同,保持空间内容变为:f\ne\nd\nc\nb\na\n#!/bin/bash;但最后一行匹配$!d,所以不删除模式空间内容,模式空间内容与保持空间相同,并默认打印模式空间内容。
2、在文件中每行后加入空行
[[email protected] bash]# sed ‘G‘ sed #!/bin/bash a b c d e f [[email protected] bash]#
3、d 删除行
[[email protected] bash]# sed ‘1d‘ sed a b c d e f [[email protected] bash]#
4、保留奇数行
[[email protected] bash]# sed ‘n;d‘ sed 1 3 5 [[email protected] bash]#
5、只打印偶数行
[[email protected] bash]# sed -n ‘n;p‘ sed 2 4 6 [[email protected] bash]#