学习资料:《linux大棚命令百篇上》
什么是sed
sed是stream editor的缩写,翻译过来就是“流编辑器”,而实际上它的作用也正如描述的那样。sed命令是一个面向行处理的工具, 以行为处理单位,可以实现很强大的处理能力。sed对每一行进行处理后输出到标准输出,反应在我们可见的角度就是,sed会将修改的内容输出到shell上显示出来,而不会对源文件做任何修改(当然如果想对源文件进行修改也是可以的)。
像学习数学一样,sed的用法可以用一个公式概括:
sed [选项] command file
其中:
- command:对文件每行(或者选定行)进行的操作。
- file:要操作的文件。
sed的工作原理
sed在按行处理文件时,会将要处理的那行放入缓冲区中,然后sed命令会对缓冲区中的内容进行处理。处理完成后将缓冲区的内容送入屏幕显示,接着处理下一行,直到文件结束。所以整个处理过程中,sed操作的都是缓冲区的内容,因而并不会对源文件进行任何修改。
下面看一个小例子来展示一下sed的魅力:
首先用一个脚本来创建一个包含行号文件
#! /bin/bash
for i in {1..5}
do
echo "hello linux $i" >> hello
done
运行脚本后,产生了一个hello文件,文件内容如下:
wangsheng@ubuntu[18:47:21]:~/Documents$ sh file.sh
wangsheng@ubuntu[18:47:26]:~/Documents$ cat hello
hello linux 1
hello linux 2
hello linux 3
hello linux 4
hello linux 5
如果我只想看文件中的第2行,就可以用sed命令这么写:
wangsheng@ubuntu[18:49:47]:~/Documents$ sed -n '2p' hello
hello linux 2
可以看到只显示了hello文件中的第2行。这个例子很简单,只是展示一下sed的基本用法,后面会对sed的用法进行详细描述。这里的-n
是sed提供的一个选项,而'2p'
就是sed的command部分表示打印第二行;hello
是file部分,表示操作的是hello这个文件。
sed的详细用法
花样的命令
命令是sed用法中command部分,sed之所以拥有强大的功能,就是因为其command部分可以组合成非常多的花样。常用的command通常由两部分构成,一部分是范围设定,也就是选定行;另一部分是动作处理,即对选定的行执行什么样的操作。
范围设定
范围设定,顾名思义就是sed命令需要对哪一行或者哪几行进行操作。值得注意的是,如果不选定行,那么默认对所有行执行此操作。常用的范围设定有如下几种形式:
- x:x为行号,表示选定第x行
#显示第2行 wangsheng@ubuntu[18:49:47]:~/Documents$ sed -n '2p' hello hello linux 2
- x,y:从第x行到第y行
#显示2-4行 wangsheng@ubuntu[19:40:50]:~/Documents$ sed -n '2,4p' hello hello linux 2 hello linux 3 hello linux 4
- x,$:从第x行到最后一行
#显示第4行到最后一行 wangsheng@ubuntu[19:44:41]:~/Documents$ sed -n '4,$p' hello hello linux 4 hello linux 5
- x,y!:不包含指定行号x到y的行
#显示除了第3行的所有行 wangsheng@ubuntu[19:47:33]:~/Documents$ sed -n '3!p' hello hello linux 1 hello linux 2 hello linux 4 hello linux 5
- /pattern/:查询包含该模式的行,这里的
pattern
可以使用正则表达式。#查询显示含有5的行 wangsheng@ubuntu[19:47:40]:~/Documents$ sed -n '/5/p' hello hello linux 5 #查询显示含有linux的行 wangsheng@ubuntu[19:50:47]:~/Documents$ sed -n '/linux/p' hello hello linux 1 hello linux 2 hello linux 3 hello linux 4 hello linux 5
- x,/pattern/ :通过行号和模式查询匹配行,表示从第x行到匹配该模式的行。
动作处理
动作处理会提供很多丰富的动作供我们选择,可以利用这些动作对某行内容进行加入、删除、修改等操作。这里介绍几个常用的动作:
-
p : 打印,将选中的那行打印输出到屏幕上。通常
p
会与参数sed -n
一起用。(关于-n
选项的作用,后面在介绍sed选项的时候会详细说明)。#源文件 wangsheng@ubuntu[20:04:00]:~/Documents$ cat hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello php 5 #显示第2行 wangsheng@ubuntu[20:04:06]:~/Documents$ sed -n '2p' hello hello java 2 #显示最后一行 wangsheng@ubuntu[20:05:38]:~/Documents$ sed -n '$p' hello hello php 5 #显示含有java的行 wangsheng@ubuntu[20:16:25]:~/Documents$ sed -n '/java/p' hello hello java 2
-
a : 增加,a 的后面可以接字符串,而这些字符串会在新的一行出现(目前的下一行),而如果想添加多行,可以使用
\n
换行符。#在文件末添加一行hello python wangsheng@ubuntu[20:08:48]:~/Documents$ sed '$a hello python' hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello php 5 hello python #使用\n可以添加换行,例如本例在含有android行的下一行添加了两行内容 wangsheng@ubuntu[20:17:39]:~/Documents$ sed '/android/a hello\nworld' hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello world hello php 5
-
c : 行替换,c 的后面可以接字符串,这些字符串可以替换 n1,n2 之间的行。
#第2行替换为hello world wangsheng@ubuntu[20:09:00]:~/Documents$ sed '2c hello world' hello hello linux 1 hello world hello c++ 3 hello android 4 hello php 5 #第3行至最后一行替换为hello world wangsheng@ubuntu[20:11:06]:~/Documents$ sed '3,$c hello world' hello hello linux 1 hello java 2 hello world
-
d : 删除行,因为是删除,所以 d 后面通常不接任何内容,表示删除选定行的内容。
#删除第1行 wangsheng@ubuntu[20:12:58]:~/Documents$ sed '1d' hello hello java 2 hello c++ 3 hello android 4 hello php 5 #删除含有java的行 wangsheng@ubuntu[20:19:05]:~/Documents$ sed '/java/d' hello hello linux 1 hello c++ 3 hello android 4 hello php 5
-
s : 替换,可以直接进行搜寻替换的工作。通常 s 可以搭配正则表达式。
格式:sed 's/要替换的字符串/新的字符串/g'
(要替换的字符串可以用正则表达式)#搜索含有java的行,并将该行的java替换成javaee wangsheng@ubuntu[20:19:49]:~/Documents$ sed '/java/s/java/javaee/g' hello hello linux 1 hello javaee 2 hello c++ 3 hello android 4 hello php 5 #也可以使用该功能删除某个字符串,例如本例将java替换成空即删掉了第2行的java wangsheng@ubuntu[20:25:13]:~/Documents$ sed '/java/s/java//g' hello hello linux 1 hello 2 hello c++ 3 hello android 4 hello php 5
有时候我们不想在当前行的下一行或者上一行添加内容,而希望在行首或行尾追加内容时怎么办呢?
其实就可以使用s功能。其中^表示行首,$表示行尾
#在每行行首添加head------ wangsheng@ubuntu[20:29:56]:~/Documents$ sed 's/^/head------/g' hello head------hello linux 1 head------hello java 2 head------hello c++ 3 head------hello android 4 head------hello php 5 #在每行行尾追加------tail wangsheng@ubuntu[20:35:12]:~/Documents$ sed 's/$/------tail/g' hello hello linux 1------tail hello java 2------tail hello c++ 3------tail hello android 4------tail hello php 5------tail #在第1行行尾追加------tail wangsheng@ubuntu[20:35:36]:~/Documents$ sed '1s/$/------tail/g' hello hello linux 1------tail hello java 2 hello c++ 3 hello android 4 hello php 5
另外,如果说s是代表替换,那么末尾的g是什么意思呢?
其实字符g代表的意思是每行出现的字符全部替换,如果想在特定字符处添加,g就有用了,否则只会替换每行第一个,而不继续往后找了。
-
r : 文件插入,可以在当前行的下一行插入指定文件中的内容。
#需要插入的文件内容 wangsheng@ubuntu[20:28:34]:~/Documents$ cat foo +++this is file in foo+++ #使用a动作插入时,只会将foo当做字符串插入 wangsheng@ubuntu[20:27:46]:~/Documents$ sed '/java/a foo' hello hello linux 1 hello java 2 foo hello c++ 3 hello android 4 hello php 5 #使用r命令插入时,会将foo代表的文件内容插入 wangsheng@ubuntu[20:28:12]:~/Documents$ sed '/java/r foo' hello hello linux 1 hello java 2 +++this is file in foo+++ hello c++ 3 hello android 4 hello php 5
好用的选项
刚才我们已经提到了sed的命令的基本构成,下面来看看sed有哪些好玩的选项。
-
-n:使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN的内容一般都会被输出到屏幕上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被输出出来。
#使用-n选项,只会输出处理的那一行 wangsheng@ubuntu[20:43:59]:~/Documents$ sed -n '/java/a ======' hello ====== #不使用-n选项,输出所有行 wangsheng@ubuntu[20:44:22]:~/Documents$ sed '/java/a ======' hello hello linux 1 hello java 2 ====== hello c++ 3 hello android 4 hello php 5
-
-e:如果我要执行的动作包含两个或以上怎么办呢?这也很简单,sed的-e属性就是支持这个功能的,只要在每个动作之前分别加上-e选项即可。
#前一个command输出1-2行,后一个command输出第4行 #每个command之前均加上-e选项就可以同时处理两个动作了 wangsheng@ubuntu[20:44:33]:~/Documents$ sed -n -e '1,2p' -e '4p' hello hello linux 1 hello java 2 hello android 4
-
-f:如果设定的command部分太长,那么可以将command部分内容写到一个单独的文件中,然后使用-f选项来指定这个文件作为我们sed命令的command部分。
#已经写好的command,存储在文件中 wangsheng@ubuntu[20:49:12]:~/Documents$ cat command /java/,/android/p #使用-f选项指定command文件 wangsheng@ubuntu[20:49:16]:~/Documents$ sed -n -f command hello hello java 2 hello c++ 3 hello android 4
-
-i:直接修改读取的文件内容,而不是输出到屏幕上。要注意此选项会直接改变源文件的内容,如果用此命令对系统配置文件修改的话,那么如果出现什么无法挽回的后果也只能怪自己作死了。
#源文件 wangsheng@ubuntu[20:49:32]:~/Documents$ cat hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello php 5 #第2行后添加新行,内容为this is a new line wangsheng@ubuntu[20:52:32]:~/Documents$ sed -i '2a this is a new line' hello #源文件被修改 wangsheng@ubuntu[20:53:20]:~/Documents$ cat hello hello linux 1 hello java 2 this is a new line hello c++ 3 hello android 4 hello php 5
sed实践——自动评分的脚本
学了知识当然是为了用的,那么我们就用一个实际的例子来看看sed的应用。
老师在服务器上为我们每个人创建了一个student_ids文件,里面有所有同学的名单,每个同学都要在该文件中为其他同学打分。为了完成评分的工作,需要使用vi打开再一个个编辑,而且还不能全打满分或者有规律地打分,于是本着能偷懒就偷懒的原则,能不能写一个自动打分脚本呢?显然,使用sed这么强大的行处理功能实现起来肯定是没问题的。
最终写出来的脚本如下:
#!/bin/bash
count=0
#统计行数
while read line
do
count=$(($count+1))
done < student_ids
for (( i=1; i<="$count"; i++ ))
do
#产生84-99之间的随机数
let score=$RANDOM%15+84
#第i行行尾追加随机数
sed -i "${i}s/$/ $score/g" student_ids
done
首先得到该文件的行数,并用一个变量count记录下来。然后循环count次,每次循环产生一个84-99之间的随机数,使用sed命令将此随机数插入到该行的行尾,然后下一次循环再对下一行执行同样的操作。值得注意的是,这里sed使用了-i选项,表示直接对源文件执行此操作,另外使用了强引用“”,而不是弱引用‘’。如果是弱引用的话,那么会将$score
部分当做是一个字符串,直接在每行行尾加上了$score
这样的字符串;而如果使用强引用的话,则直接将$score
代表的随机数插入到行尾。
最后效果如下:
#student_ids源文件
wangsheng@ubuntu[21:03:12]:~/Documents$ cat student_ids
142006010124
142006010125
142006010126
142006010127
142006010128
142006010130
142006010121
142006010122
#执行脚本
wangsheng@ubuntu[21:03:27]:~/Documents$ sh autograding.sh
8 lines completed!
#student_ids文件已经被修改
wangsheng@ubuntu[21:03:32]:~/Documents$ cat student_ids
142006010124 98
142006010125 90
142006010126 94
142006010127 89
142006010128 96
142006010130 84
142006010121 94
142006010122 98