shell脚本编程系列
学习sed编辑器
sed编辑器被称作流编辑器(stream editor),与普通的交互式文本编辑器不同,在交互式文本编辑器可以用键盘命令交互式插入、删除或替换文本数据。流编辑器则是根据事先设计好的一组规则编辑数据流。
sed编辑器主要操作
- 从输入中读取一行数据
- 根据所提供的编辑器命令匹配数据
- 按照命令修改数据流中的数据
- 将新的数据输出到STDOUT
- 按顺序逐行按照以上步骤处理数据
sed命令的格式为
sed options script file
options参数允许修改sed命令的行为,比如
- -n不产生输出,使用p(print)命令完成输出
- -f file在处理输入时,将file中指定的命令添加到已有的命令中
- -e commands则是在处理输入时,加入额外的sed命令。
script参数指定了应用于流数据中的单个命令,如果需要多个命令,则使用-e选项指定,也可以使用-f选项在单独的文件中指定。
在命令行中定义编辑器命令
echo "This is a test" | sed 's/test/big test/'
使用s替换命令,替换命令会用斜线间指定的第二个字符串替换第一个字符串,本例中,big test替换了test
sed 's/dog/cat/' data1.txt
读取文件,修改其中的内容,然后发送到STDOUT
在命令行中使用多个编辑器命令
sed -e 's/brown/red/;s/dog/cat/' data1.txt
两个命令都应用于文件的每一行数据。命令之间必须以分号分隔,并且在命令末尾和分号之间不能出现空格
如果不想再多个命令之间使用分号,也可以使用次提示符来分隔命令。必须在闭合单引号所在行结束命令,bash shell一旦发现了闭合单引号,就会执行命令
[root@192 Chapter19-Scripts]# sed -e '
> s/brown/green/
> s/fox/toad/
> s/dog/cat/' data1.txt
从文件中读取编辑器命令
sed -f script1.sed data1.txt
脚本中的内容为
s/brown/green/
s/fox/toad/
s/dog/cat/
在这种情况下,不用在每条命令后面加分号
sed编辑器基础命令
更多的替换选项
替换标志
在默认情况下,替换命令s只替换每行中出现的第一处匹配文本,要想替换所有的匹配文本,则必须使用替换标志(substitution flag)。替换标志在替换命令字符串之后设置,其格式为
s/pattern/replacement/flags
有4种可用的替代标志
数字:指明新文本将被替换行中的第几处匹配
- g:替换行中所有的匹配
- p:指明打印出替换后的行
- w file:将替换后的结果写入文件
替换每行中第二处匹配文本
sed 's/test/trial/2' data4.txt
替换文本行中所有的匹配文本
sed 's/test/trial/g' data4.txt
sed -n 's/test/trial/p' data5.txt
替换标志p会打印出包含替换命令中指定匹配模式的文本行,通常会和-n选项配合使用,-n选项会抑制sed编辑器的输出,而替换标志p会输出替换后的行。二者配合只会输出被替换命令修改过的行
sed -n 's/test/trial/w test.txt' data5.txt
替换标志w会将匹配的行输出保存到指定文件中。sed编辑器的正常输出会被保存在STDOUT中,只有那些包含匹配模式的行会被保存在指定的输出文件中。
替换字符
字符串中有一些不太方便在替代模式中使用的字符,比如正斜线(/),因为正斜线被用作替换命令的分隔符,因此它在匹配模式和替换文本中同时出现时,必须使用反斜线来转义。
比如
sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
但是这样很容易造成混乱和错误,因此sed编辑器允许选择其他字符作为替换命令的替代分隔符
sed 's!/bin/bash!/bin/csh!' /etc/passwd
使用感叹号作为替换命令的分隔符
使用地址
在默认情况下,在sed编辑器中使用的命令会应用于所有的文本行,如果只想将命令应用于特定的某一行或某些行,则必须使用行寻址。
在sed命令中有两种形式的行寻址:
- 以数字形式表示的行区间
- 匹配行内文本的模式
以上两种形式使用相同的格式来指定地址
[address] command
也可以将针对特定地址的多个命令分组:
address{
command1
command2
command3
}
sed编辑器会将指定的各个命令应用于匹配指定地址的文本行
- 数字形式的行寻址
行地址既可以是单个行号
sed '2s/dog/cat/' data1.txt
也可以是用起始行号、逗号以及结尾行号指定的行区间
sed '2,3s/dog/cat/' data1.txt
美元符号作为结尾行号
sed '2,$s/dog/cat/' data1.txt
- 使用文本模式过滤
/pattern/command
必须将指定的模式(pattern)放在正斜线内
比如只想修改test用户的默认shell
sed '/test/s/bash/csh/' /etc/passwd
上面的案例使用固定的文本模式过滤特定的值,但有局限,sed编辑器也支持在文本模式中使用正则表达式来创建匹配效果更好的模式
- 使用组的方式替换文本
[root@192 Chapter19-Scripts]# sed '2{
> s/fox/toad/
> s/dog/cat/
> }' data1.txt
删除行
删除命令(d)会删除匹配指定模式的所有行。使用时需要小心,如果没有指定寻址方式,则流中的所有文本都会被删除。
sed 'd' data1.txt
- 通过行号指定要删除的行
- 通过特定行区间指定
sed '2,3d' data6.txt
- 通过特殊的末位字符$指定位置
sed '3,$d' data6.txt
sed编辑器的模式匹配特性也适用于删除命令
sed '/number 1/d' data6.txt
也可以使用两个文本模式来删除匹配区间的行,但是要注意,第一个模式启动行删除功能,而第二个模式关闭行删除功能,如果匹配了第一个模式之后,剩余文本中没有匹配第二个模式的,则从匹配了第一个模式的行之后,都会被删除
插入和附加文本
sed '[address]command new line'
插入(insert)(i)命令会在指定行前增加一行
echo 'Test Line 2' | sed 'i\Test line 1'
追加(append)(a)命令会在指定行后增加一行
echo 'Test Line 2' | sed 'a\Test line 1'
要向数据流内部插入或附加数据,必须用地址告诉sed编辑器希望数据出现在什么位置。使用行号或文本模式都行,但不能用行区间。
sed '3i\This is an inserted line.' data6.txt
会在文本的第3行之前插入数据
sed '3a\This is an inserted line.' data6.txt
会在文本的第3行之后插入数据
sed '$a\This is an inserted line.' data6.txt
直接附加到行尾
修改行
修改(c)命令允许修改数据流中整行文本的内容,跟插入和追加命令的工作机制一样,必须在sed命令中单独指定一行
修改第二行的文本
sed '2c\This is a changed line of test.' data6.txt
文本模式会修改匹配的任意文本行
sed '/have 6 Infinity Stones/c\Snap!This is changed line of text.' data8.txt
使用地址区间需要小心,结果是将多行修改成了一行,而不是将匹配的多行都修改
转换命令
转换命令(y)是唯一可以处理单个字符的sed编辑器命令
[address]y/inchars/outchars
转换命令会对inchars和outchars进行一对一的映射。inchars中的第一个字符会被转换成第一个字符,inchars中的第二个字符会被转换为第二个字符,这个映射过程会一直持续到处理完指定字符。如果inchars和outchars的长度不同,则sed编辑器会产生一条错误消息。
转换命令是一个全局命令,会对文本中所有匹配到的字符进行转换,不考虑字符出现的位置
echo "Test #1 of try #1." | sed 'y/123/678/'
再探打印
- 打印命令(p)用于打印文本行
echo "This is a test" | sed 'p'
打印出所有的文本
sed -n '/3rd line/p' data6.txt
打印出包含匹配文本模式的行
sed -n '2,3p' data6.txt
打印出数据流中的部分行
sed -n '/3/{p;s/line/test/p}' data6.txt
在使用替换或修改命令做出改动之前查看相应的行
- 等号(=)命令用于打印行号
sed -n '/text/{=;p}' data7.txt
数据流中查找特定文本,然后打印行号和内容
- 列出(l)命令用于列出行
列出命令可以打印数据流中的文本和不可打印字符
sed -n 'l' data10.txt
使用sed处理文件
- 写入文件
写入(w)命令用来向文件写入行,命令格式如下:
[address]w filename
filename可以使用相对路径或绝对路径,运行sed编辑器的用户必须具有文件的写权限。地址可以是sed支持的任意类型的寻址方式,比如单个行号、文本模式、行区间或文本模式区间。
sed '1,2w test.txt' data6.txt
将数据流的前两行写入文本文件
sed -n '1,2w test.txt' data6.txt
不在STDOUT中显示文本行,可以使用sed的-n选项
- 将匹配文本模式的数据行写入目标文件
sed -n '/Browncoat/w Browncoats.txt' data12.txt
- 从文件读取数据
可以通过i或者a向数据流中插入或追加内容,而读取(r)命令允许将一条独立文件中的数据插入数据流,格式如下
[address]r filename
filename参数指定了数据文件的绝对路径或相对路径。读取命令中无法使用地址区间,只能指定单个行号或文本模式地址。sed编辑器会将文件内容插入指定地址之后。
sed '3r data13.txt' data6.txt
要在数据流的末尾添加文本,只需要使用美元符号地址即可
sed '$r data13.txt' data6.txt
使用文本模式地址将数据文件中的所有行插入数据流
读取命令还有一个很酷的用法是和删除命令配合使用,利用另一个文件中的数据来替换文件中的占位文本
[root@192 Chapter19-Scripts]# cat notice.std
Would the following people:
LIST
please report to the ship's captain.
[root@192 Chapter19-Scripts]# cat data12.txt
Blum, R Browncoat
McGuiness, A Alliance
Bresnahan, C Browncoat
Harken, C Alliance
[root@192 Chapter19-Scripts]# sed '/LIST/{
> r data12.txt
> d
> }' notice.std