linux命令学习(二)——sed

学习资料:《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

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