Linux文本三剑客之sed命令


纸上得来终觉浅,绝知此事要躬行。

sedStream EDitor的首字母缩写,是一个命令行版的文本行编辑器。默认工作方式:默认不会编辑源文件,而是一次读取一行存放到sed的内存缓冲区中,即模式空间(pattern space)中进行编辑操作,将编辑之后的内容默认打印到屏幕上。

Linux文本三剑客之sed命令


1. 命令用法

格式:sed [option]... 'script' inputfile...

  • option
    • -n
      • 不输出模式空间中的内容至屏幕,除非自定义输出到屏幕
    • -e
      • 多点编辑
      • 这里的多点操作,就是可以执行多个sed语句,使用-e连接
    • -f /PATH/TO/SCRIPT_FILE
      • 从指定文件中读取编辑脚本
      • 这里的编辑脚本一般为每行一个
    • -r
      • 支持使用扩展正则表达式
    • -i
      • 原处编辑
  • script
    • 地址定界编辑命令
      • '/^#/d'表示删除空行

2. 地址定界

(1) 不给地址

  • 对全文进行处理

(2) 单地址

  • #
    • 指定的行
  • /pattern/
    • 被此处模式所能够匹配到的每一行

(3) 地址范围

  • #,#
    • 第几行到第几行之间
  • #,+#
    • 相对地址定界,第几行到之后的几行之间
  • /pat1/,/pat2/
    • pat1匹配到的行到被pat2匹配到的行之间
  • #,/pat1/
    • 第几行到精确被pat1匹配到的行之间

(4) 步进 ~

  • 1~2
    • 表示1+21+2+2等等
  • 2~2
    • 表示2+22+2+2等等

3. 编辑命令

命令

  • d:
    • 删除
    • 需要和地址定界一起使用
  • p
    • 显示模式空间中的内容
    • 需要和地址定界一起使用
  • a \text
    • 对所匹配到的行后面追加text的内容
    • \表示转义字符
    • 支持使用\n实现多行追加
  • i \text
    • 对所匹配到的行前面插入text的内容
    • \表示转义字符
    • 支持使用\n实现多行插入
  • c \text
    • 对所匹配到的行替换text的内容
    • \表示转义字符
  • w /path/to/somefile
    • 保存模式空间匹配到的行至指定文件中
  • r /path/from/somefile
    • 读取指定文件的文本流至模式空间中匹配到的行的行后
  • =
    • 为模式空间中的行打印行号
  • !
    • 取反条件
    • 放在编辑命令之前的位置
  • s///
    • 支持使用其它分隔符,s@@@/s###,后跟替换标记

替换标记

  • g: 行内全局替换
  • p: 显示替换成功的行
  • w /PATH/TO/SOMEFILE:将替换成功的结果保存至指定文件中

4. 实战演示

  • 编辑命令
# 这里的删除都是没有修改源文件的,只是定向输出到屏幕上

# 把/etc/fstab中UUID开头的文件进行删除
[root@localhost ~]# sed '/^UUID/d' /etc/fstab

# 把/etc/fstab中#开头的文件进行删除
[root@localhost ~]# sed '/^#/d' /etc/fstab

# 删除/etc/fstab中的空白行
[root@localhost ~]# sed '/^$/d' /etc/fstab

# 删除/etc/fstab中的1-10行内容
[root@localhost ~]# sed '1,10d' /etc/fstab
# p命令会对匹配到的地址输出两遍,因为默认就会显示模式空间中的内容
[root@localhost ~]# sed '1,10p' /etc/fstab
[root@localhost ~]# sed '/^UUID/p' /etc/fstab

# 使用禁止打印就可以避免了
# 其中sed '/^UUID/d'和sed -n '/^UUID/p'正好相反
[root@localhost ~]# sed -n '1,10p' /etc/fstab
[root@localhost ~]# sed -n '/^UUID/p' /etc/fstab
# a命令,在10-13行之后追加# hello world
[root@localhost ~]# sed '10,13a \# hello world' /etc/fstab
...
UUID=6d0f6433-eed7-4cb2-81f5-2c4feaad2c18   /boot   ext4    defaults    1 2
# hello world
/dev/mapper/VolGroup-lv_swap swap                   swap    defaults    0 0
# hello world
tmpfs                   /dev/shm                    tmpfs   defaults    0 0
# hello world
...

# 支持多行追加
[root@localhost ~]# sed -n '10,13a \# hello world.\nWelcome' /etc/fstab
# i命令,在10-13行之前追加# hello world
[root@localhost ~]# sed '10,13i # hello world.\nWelcome' /etc/fstab
# i命令,把UUID开头的行替换成为# hello world
[root@localhost ~]# sed '/^UUID/c # hello world.\nWelcome' /etc/fstab
...
/dev/mapper/VolGroup-lv_root    /            ext4    defaults        1 1
# hello world.
Welcome
/dev/mapper/VolGroup-lv_swap    swap        swap    defaults        0 0
tmpfs                          /dev/shm     tmpfs   defaults        0 0
...
# w命令将UUID开头的行保存在/tmp/fstab.txt中
[root@localhost ~]# sed -n '/^UUID/w /tmp/fstab.txt' /etc/fstab
# =命令是为匹配到的行打印行号
[root@localhost ~]# sed '10,13=' /etc/fstab
...
/dev/mapper/VolGroup-lv_root            /           ext4    defaults        1 1
10
UUID=6d0f6433-eed7-4cb2-81f5-2c4fea18   /boot       ext4    defaults        1 2
11
/dev/mapper/VolGroup-lv_swap            swap        swap    defaults        0 0
12
tmpfs                                   /dev/shm    tmpfs   defaults        0 0
13
devpts                                  /dev/pts    devpts  gid=5,mode=620  0 0
...
# r命令将匹配到的行之后添加指定文件的内容
[root@localhost ~]# sed '6r /etc/issue' /etc/fstab
...
# Accessible filesystems, by reference, are maintained under '/dev/disk'
CentOS release 6.7 (Final)
Kernel \r on an \m
...
# !表示取反,对匹配到开头为UUID的行不删除,其他都删除
[root@localhost ~]# sed '/^UUID/!d' /etc/fstab
UUID=6d0f6433-eed7-4cb2-81f5-2c4feaad2c18   /boot    ext4    defaults    1 2
# s命令表示查找替换
[root@localhost ~]# sed 's@^UUID@uuid@' /etc/fstab
...
uuid=6d0f6433-eed7-4cb2-81f5-2c4feaad2c18   /boot    ext4    defaults    1 2
...

[root@localhost ~]# sed -n 's@^UUID@uuid@p' /etc/fstab
uuid=6d0f6433-eed7-4cb2-81f5-2c4feaad2c18   /boot    ext4    defaults    1 2
# s命令表示查找替换
# 这里的&表示替代r..t,这里默认只替换每行中第一次匹配到的r..t
[root@localhost ~]# sed '[email protected]@&er@' /etc/passwd
rooter:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
...

# 全局替换
# 如果加-n表示都不输出
# 如果加-n -r表示原文全局替换
[root@localhost ~]# sed '[email protected]@&er@g' /etc/passwd
rooter:x:0:0:rooter:/rooter:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
# -e表示多点编辑,第一个编辑命令y将前三行中的所有小写字母替换为大写字母
# -n表示不显示替换后的输出,第二个编辑命令将只是打印输出转换后的前三行
# 注意y不能用于正则
[root@localhost ~]# sed -n -e '1,3y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' -e '1,3p' testfile
NORTHWEST       NW     CHARLES MAIN         3.0     .98     3       34
WESTERN         WE     SHARON GRAY          5.3     .97     5       23
SOUTHWEST       SW     LEWIS DALSASS        2.7     .8      2       18
# s命令表示查找替换
[root@localhost ~]# sed '[email protected]@&er@w /tmp/passwd.txt' /etc/passwd
# 奇数行
[root@localhost ~]# sed -n '1~2p' test.txt
1
3
5

# 偶数行
[root@localhost ~]#  sed -n '2~2p' test.txt
2
4
6
  • 上手练习
# 练习1
# 在centos7中才有grub2.cfg
# 删除/boot/grub/grub.conf文件中所有以空白开头的行行首的空白字符
sed 's@^[[:space:]]\+@@' /etc/grub2.cfg

# 练习2
# 删除/etc/fstab文件中所有以#开头,后面至少跟一个空白字符的行的行首的#和空白字符
sed 's@^#[[:space:]]\+@@' /etc/fstab

# 练习3
# echo一个绝对路径给sed命令,取出其基名,取出其目录名
echo "/etc/sysconfig/" | sed 's@[^/]\+/\?$@@'

5. 高级编辑

保持空间

  • 即为hold spcaesed的内存中不仅有模式空间还有一个保持空间
  • 保持空间不做任何处理,只会在模式空间中删除

使用情况

  • 如果我们在模式空间中处理的半成品内容可以放到保持空间进行存放,之后在使用这个半成品的时候,只需要取出就可以了

命令

  • h - 把模式空间中的内容覆盖至保持空间中,模式空间中的这行仍存在
  • H - 把模式空间中的内容追加至保持空间中,模式空间中的这行仍存在
  • g - 从保持空间取出数据覆盖至模式空间中,保持空间中的这行仍存在
  • G - 从保持空间取出数据追加至模式空间中,保持空间中的这行仍存在
  • x - 把模式空间中的内容与保持空间中的内容进行互换
  • n - 读取被匹配到的行的下一行至模式空间,把下一行替代匹配到的那一行,只有一行
  • N - 追加匹配到的行的下一行至模式空间,把下一行追加到匹配到的那一行之后,有两行
  • d - 删除模式空间中的单行
  • D - 删除模式空间中的所有行

实战演示

# cat test.txt
1
2
3
4
5
6
# 显示偶数行
sed -n 'n;p' FILE

# sed -n 'n;p' test.txt
2
4
6
# 逆向显示文件内容
# 1!表示除了第一行
# G表示从保持空间取出内容追加至模式空间
# h表示把模式空间中的内容覆盖至保持空间中
# $!表示除了最后一行
# d删除模式空间中的单行
# $!d表示不是最后一行才删除,是最后一行不删除,因为sed的特定,所以未删除就会打印模式空间中的内容
sed '1!G;h;$!d' FILE

# sed '1!G;h;$!d' test.txt
6
5
4
3
2
1

# 读取test.txt的第一行,将1放到模式空间中,判断是1所以不执行G操作。接着执行h操作,将1覆盖到保持空间中,保持空间为1,模式空间还是有1存在的。判断模式空间中的1不是最后一行,执行删除操作。模式空间为空,保持空间还有1存在的。

# 读取test.txt的第二行,将2放到模式空间中,判断是2并不是1所以不执行G操作,将保持空间中的1取出追加至模式空间,现在模式空间的顺序为2、1。接着执行h操作,将2、1覆盖到保持空间中,保持空间为2、1,模式空间还是有2、1存在的。判断模式空间中的2不是最后一行,执行删除操作。模式空间为空,保持空间还有2、1存在的。

# 以此类推,即可。
# 取出文件后两行
# N表示追加匹配到的行的下一行至模式空间,把下一行追加到匹配到的那一行之后,有两行
# D表示删除多行模式空间中的所有行
sed '$!N;$!D' FILE

# sed '$!N;$!D' test.txt
5
6

# 先执行$!N表示不是最后一行的话在读取下一行到模式空间中,之后再执行$!D表示不是最后一行的话就删除所有行
# 取出文件最后一行
sed '$!d' FILE

# sed '$!d' test.txt
6
# 每一行之后加一个空行
# 在读取文件的时候,因为保持空间中没空,所以会默认读取一个空行
sed 'G' FILE

# sed 'G' test.txt
1

2

3

4

5

6
# 表示全部替换为空白行
sed 'g' FILE
# 将多个空白行合并为一个空白行
# /^$/d表示如果是空白行删除,G表示再添加一个空白行
sed '/^$/d;G' FILE

# sed '/^$/d;G' test.txt
1

2

3

4

5

6
# 显示奇数行
sed 'n;d' FILE
# 逆向显示文件中的每一行
sed -n '1!G;h;$p' FILE

# sed -n '1!G;h;$p' test.txt
6
5
4
3
2
1

# 可以使用sed '1!G;h;$p' test.txt来查看过程

文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录