正则表达式与文件格式化处理(2)-sed工具(主)

它的前一篇 正则表达式与文件格式化处理(1)-基础正则表达式练习(主)

基础正则表达式字符汇整

我们可以将基础的正则表达式特殊字符汇整如下:

RE 字符 意义与范例
^word 意义:待搜寻的字串( word )在行首!
范例:搜寻行首为 # 开始的那一行,并列出行号
grep -n '^#' regular_express.txt
word\(|意义:待搜寻的字串( word )在行尾!<br>范例:将行尾为 ! 的那一行打印出来,并列出行号<br> `grep -n '!\)' regular_express.txt`
. 意义:代表 “ 一定有一个任意字符 ” 的字符!
范例:搜寻的字串可以是 ( eve ) ( eae ) ( eee ) ( e e ), 但不能仅有 ( ee ) !
亦即 e 与 e 中间 一定 仅有一个字符,而空白字符也是字符!
grep -n 'e.e' regular_express.txt
|意义:跳脱字符,将特殊符号的特殊意义去除!
范例:搜寻含有单引号 ' 的那一行!
grep -n \' regular_express.txt
* 意义:重复零个到无穷多个的前一个 RE 字符
范例:找出含有 ( es ) ( ess ) ( esss ) 等等的字串,
注意,因为 * 可以是 0 个,所以 es 也是符合带搜寻字串。
另外,因为 * 为重复 “ 前一个 RE 字符 ” 的符号, 因此,在 * 之前必须要紧接着一个 RE 字符喔!例如任意字符则为 .*
grep -n 'ess*' regular_express.txt
[list] 意义:字符集合的 RE 字符,里面列出想要撷取的字符!
范例:搜寻含有 ( gl ) 或 ( gd ) 的那一行,需要特别留意的是,在 [] 当中 “ 谨代表一个待搜寻的字符 ” ,
例如 “a[afl]y ” 代表搜寻的字串可以是 aay, afy, aly 即 [afl] 代表 a 或 f 或 l 的意思!
grep -n 'g[ld]' regular_express.txt
[n1-n2] 意义:字符集合的 RE 字符,里面列出想要撷取的字符范围!
范例:搜寻含有任意数字的那一行!需特别留意,在字符集合 [] 中的减号 - 是有特殊意义的,
他代表两个字符之间的所有连续字符!但这个连续与否与 ASCII 编码有关,
因此,你的编码需要设置正确(在 bash 当中,需要确定LANG 与 LANGUAGE 的变量是否正确!) 例如所有大写字符则为 [A-Z]
grep -n '[A-Z]' regular_express.txt
[^list] 意义:字符集合的 RE 字符,里面列出不要的字串或范围!
范例:搜寻的字串可以是 ( oog ) ( ood ) 但不能是 ( oot ) ,那个 ^ 在 [] 内时,代表的意义是 “ 反向选择 ” 的意思。
例如,我不要大写字符,则为 [^A-Z] 。但是,需要特别注意的是,如果以 grep -n [^A-Z] regular_express.txt 来搜寻,
却发现该文件内的所有行都被列出,为什么?
因为这个 [^A-Z] 是 “ 非大写字符 ” 的意思, 因为每一行均有非大写字符,例如第一行的 "Open Source" 就有 p,e,n,o.... 等等的小写字
grep -n 'oo[^t]' regular_express.txt
\ 意义:连续 n 到 m 个的 “ 前一个 RE 字符 ”
意义:若为 {n} 则是连续 n 个的前一个 RE 字符,
意义:若是 {n,} 则是连续 n 个以上的前一个 RE 字符!
范例:在 g 与 g 之间有 2 个到 3 个的 o 存在的字串亦即( goog )( gooog )
grep -n 'go\{2,3\}g' regular_express.txt

再次强调: 正则表达式的特殊字符 与一般在指令列输入指令的 万用字符 并不相同,
例如,在万用字符当中的 * 代表的是 0 ~ 无限多个字符 的意思,但是在正则表达式当中,
* 则是 重复 0 到无穷多个的前一个 RE 字符 的意思~使用的意义并不相同,不要搞混了!

例题

我们要找到含有以 a 为开头的文件,则必须要这样:

ls | grep -n '^a.*'

ls -l 配合 grep 找出 /etc/ 下面文件类型为链接文件属性的文件名:
由于 ls -l 列出链接文件时标头会是 lrwxrwxrwx ,因此使用如下的指令即可找出结果:

ls -l /etc | grep '^l'

sed 工具

我们先来谈一谈 sed 好了, sed 本身也是一个管线命令,可以分析 standard input 的啦!
而且 sed 还可以将数据进行取代、删除、新增、撷取特定行等等的功能呢!

sed [-nefr] [ 动作 ]

选项与参数:

  • -n :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到屏幕上。
    但如果加上 -n 参数后,则只有经过 sed 特殊处理的那一行(或者动作)才会被列出来。
  • -e :直接在指令列模式上进行 sed 的动作编辑;
  • -f :直接将 sed 的动作写在一个文件内, -f filename 则可以执行 filename 内的 sed 动作;
  • -r :sed 的动作支持的是延伸型正则表达式的语法。(默认是基础正则表达式语法)
  • -i :直接修改读取的文件内容,而不是由屏幕输出。
    动作说明: [n1[,n2]]function
    n1, n2 :不见得会存在,一般代表“选择进行动作的行数”,举例来说,如果我的动作
    是需要在 10 到 20 行之间进行的,则“ 10,20[动作行为] ”
    function 有下面这些咚咚:
  • a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
  • c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
  • d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
  • i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
  • p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
  • s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正则表达式!
    例如 1,20s/old/new/g 就是啦!

以行为单位的新增 / 删除功能

范例一:将 /etc/passwd 的内容列出并且打印行号,同时,请将第 2~5 行删除!

nl /etc/passwd | sed '2,5d'
1  root:x:0:0:root:/root:/bin/bash
6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
...后面的省略...

看到了吧? sed 的动作为 '2,5d' ,那个 d 就是删除!因为 2-5 行给他删除了,所以显示的数据就没有 2-5 行啰~
另外,注意一下,原本应该是要下达 sed -e 才对,没有 -e 也行啦!同时也要注意的是, sed 后面接的动作,请务必以 ''两个单引号括住喔!

范例二:承上题,在第二行后(亦即是加在第三行)加上 “drink tea?” 字样!

nl /etc/passwd | sed '2a drink tea'
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
drink tea
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     ...

嘿嘿!在 a 后面加上的字串就已将出现在第二行后面啰!那如果是要在第二行前呢?
nl /etc/passwd | sed '2i drink tea'就对啦!就是将 a 变成 i 即可。
增加一行很简单,那如果是要增将两行以上呢?

范例三:在第二行后面加入两行字,例如 “Drink tea or .....” 与 “drink beer?”

$ nl /etc/passwd | sed '2a Drink tea or ......\
> drink bear?'
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
Drink tea or ......
drink bear?
     3  bin:x:2:2:bin:/bin:/usr/sbin/nologin
     ...

这个范例的重点是 “ 我们可以新增不只一行喔!可以新增好几行 ” 但是每一行之间都必须要以反斜线 \ 来进行新行的增加喔!
所以,上面的例子中,我们可以发现在第一行的最后面就有 \ 存在啦!在多行新增的情况下, \ 是一定要的喔!

以行为单位的取代与显示功能

范例四:我想将第 2-5 行的内容取代成为 “No 2-5 number” 呢?

nl /etc/passwd | sed '2,5c No 2-5 number'
     1  root:x:0:0:root:/root:/bin/bash
No 2-5 number
     6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
     ...

范例五:仅列出 /etc/passwd 文件内的第 5-7 行

nl /etc/passwd | sed -n '5,7p'
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/usr/sbin/nologin
     7  man:x:6:12:man:/var/cache/man:/usr/sbin/nologin

上述的指令中有个重要的选项 “ -n ” ,按照说明文档,这个 -n 代表的是 “ 安静模式 ” !
那么为什么要使用安静模式呢?你可以自行下达 sed'5,7p' 就知道了 ( 5-7 行会重复输出)!
有没有加上 -n 的参数时,输出的数据可是差很多的喔!你可以通过这个 sed 的以行为单位的显示功能,
就能够将某一个文件内的某些行号捉出来查阅!很棒的功能!不是吗?

部分数据的搜寻并取代的功能

除了整行的处理模式之外, sed 还可以用行为单位进行部分数据的搜寻并取代的功能喔!
基本上 sed 的搜寻与取代的与 vi 相当的类似!他有点像这样:
sed 's/ 要被取代的字串 / 新的字串 /g'

我们使用下面这个取得 IP 数据的范例,
一段一段的来处理给您瞧瞧,让你了解一下什么是咱们所谓的搜寻并取代吧!
步骤一:先观察原始讯息,利用 /sbin/ifconfig 查询 IP 为何?

/sbin/ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.7  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 240e:a4:3647:e800:1512:c1c:f1a5:af54  prefixlen 64  scopeid 0x0<global>
        inet6 240e:a4:3647:e800:2cc8:2a44:26f4:d06e  prefixlen 128  scopeid 0x0<global>
        inet6 fe80::1512:c1c:f1a5:af54  prefixlen 64  scopeid 0xfd<compat,link,site,host>
        ...

我们的重点在第二行,也就是 192.168.1.7 那一行而已!先利用关键字捉出那一行!

步骤二:利用关键字配合 grep 撷取出关键的一行数据

$ /sbin/ifconfig eth0 | grep 'inet '
        inet 192.168.1.7  netmask 255.255.255.0  broadcast 192.168.1.255

步骤三:将 IP 前面的部分予以删除

$ /sbin/ifconfig eth0 | grep 'inet ' | sed 's/^.*inet //g'
192.168.1.7  netmask 255.255.255.0  broadcast 192.168.1.255

步骤四:将 IP 后面的部分予以删除

$ /sbin/ifconfig eth0 | grep 'inet ' | sed 's/^.*inet //g'\
> |sed 's/ *netmask.*$//g'
192.168.1.7

继续研究 sed 与正则表达式的配合练习!
步骤一:先使用 grep 将关键字 MAN 所在行取出来

[dmtsai@study ~]$ cat /etc/man_db.conf | grep 'MAN'
# MANDATORY_MANPATH manpath_element
# MANPATH_MAP path_element manpath_element
# MANDB_MAP global_manpath [relative_catpath]
# every automatically generated MANPATH includes these fields
....(后面省略)....

步骤二:删除掉注解之后的数据!

[dmtsai@study ~]$ cat /etc/man_db.conf | grep 'MAN'| sed 's/#.*$//g'
MANDATORY_MANPATH /usr/man
....(后面省略)....

从上面可以看出来,原本注解的数据都变成空白行啦!所以,接下来要删除掉空白行

[dmtsai@study ~]$ cat /etc/man_db.conf | grep 'MAN'| sed 's/#.*$//g' | sed '/^$/d'
MANDATORY_MANPATH /usr/man
MANDATORY_MANPATH /usr/share/man
MANDATORY_MANPATH /usr/local/share/man
....(后面省略)....

直接修改文件内容(危险动作)

你以为 sed 只有这样的能耐吗?那可不! sed 甚至可以直接修改文件的内容呢!

范例六:利用 sed 将 regular_express.txt 内每一行结尾若为 . 则换成 !

sed -i 's/\.$/\!/g' regular_express.txt

上头的 -i 选项可以让你的 sed 直接去修改后面接的文件内容而不是由屏幕输出喔!
这个范例是用在取代!请您自行 cat 该文件去查阅结果啰!

范例七:利用 sed 直接在 regular_express.txt 最后一行加入 “# This is a test”

sed -i '$a # This is a test' regular_express.txt

由于 $ 代表的是最后一行,而 a 的动作是新增,因此该文件最后新增啰!

sed 的 “ -i ” 选项可以直接修改文件内容,这功能非常有帮助!举例来说,如果你有一个 100 万行的文件,
你要在第 100 行加某些文字,此时使用 vim 可能会疯掉!因为文件太大了!那怎办?
就利用 sed 啊!通过 sed 直接修改 / 取代的功能,你甚至不需要使用 vim 去修订!很棒吧!

它的后一篇:正则表达式与文件格式化处理(3)-正则表达式延伸,awk(主)

参考: <<鸟哥的Linux私房菜-基础学习篇(第四版)>>

posted @ 2019-12-29 21:26  enjoy_jun  阅读(416)  评论(0编辑  收藏  举报