Linux(9):正则表达式

目录

1 基础正则表达式

1.1 语系对正则表达式的影响

1.2 grep 的一些高级选项

1.3 基础正则表达式练习

1.3.1 搜寻特定字串

1.3.2 利用中括号 [ ] 来搜寻集合字符

1.3.3 行首与行尾字符 ^ $

1.3.4 任意一个字符 . 与重复字符 *

1.3.5 限定连续 RE 字符范围 {}

1.4 基础正则表达式字符汇整 (characters)

1.5 sed 工具

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

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

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

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

2 扩展正则表达式

3 文件的格式化与相关处理

3.1 格式化打印: printf

3.2 awk:好用的数据处理工具

3.3 文件比对工具

3.3.1 diff

3.3.2 cmp

3.3.3 patch

3.4 文件打印准备: pr


       正则表达式 (Regular Expression, RE, 或称为常规表达式)是通过一些特殊字符的排列,用以“搜寻/取代/删除”一列或多列文字字串, 简单的说,正则表达式就是用在字串的处理上面的一项“表示式”。正则表达式并不是一个工具程序, 而是一个字串处理的标准依据,如果您想要以正则表达式的方式处理字串,就得要使用支持正则表达式的工具程序才行, 这类的工具程序很多,例如 vi, sed, awk 等等。但例如 cp, ls 等指令并未支持正则表达式, 所以就只能使用 bash 自己本身的万用字符而已。

         正则表达式与通配符是完全不一样的东西!通配符(wildcard)代表的是 bash 操作接口的一个功能, 但正则表达式则是一种字串处理的表示方式!

1 基础正则表达式

        正则表达式也需要支持工具程序来辅助才行,这里就先介绍一个最简单的字串撷取功能的工具程序,那就是 grep 。

1.1 语系对正则表达式的影响

       文件其实记录的仅有 0 与 1,我们看到的字符文字数字都是通过编码表转换来的。由于不同语系的编码数据并不相同,所以就会造成数据撷取结果的差异了。 举例来说,在英文大小写的编码顺序中,zh_CN.big5 及 C 这两种语系的输出结果分别如下:

  • LANG=C 时:0 1 2 3 4 ... A B C D ... Z a b c d ...z
  • LANG=zh_CN 时:0 1 2 3 4 ... a A b B c C d D ... z Z

      所以,使用正则表达式时,需要特别留意当时环境的语系为何, 否则可能会发现与别人不相同的选取结果。

特殊符号代表意义
[:alnum:]代表英文大小写字符及数字,亦即 0-9, A-Z, a-z
[:alpha:]代表任何英文大小写字符,亦即 A-Z, a-z
[:blank:]代表空白键与 [Tab] 按键两者
[:cntrl:]代表键盘上面的控制按键,亦即包括 CR, LF, Tab, Del.. 等等
[:digit:]代表数字而已,亦即 0-9
[:graph:]除了空白字符 (空白键与 [Tab] 按键) 外的其他所有按键
[:lower:]代表小写字符,亦即 a-z
[:print:]代表任何可以被打印出来的字符
[:punct:]代表标点符号 (punctuation symbol),亦即:" ' ? ! ; : # $...
[:upper:]代表大写字符,亦即 A-Z
[:space:]任何会产生空白的字符,包括空白键, [Tab], CR 等等
[:xdigit:]代表 16 进位的数字类型,因此包括: 0-9, A-F, a-f 的数字与字符

     尤其上表中的[:alnum:], [:alpha:], [:upper:], [:lower:], [:digit:] 这几个一定要知道代表什么意思,因为他要比 a-z 或 A-Z 的用途更确定。

1.2 grep 的一些高级选项

      [dmtsai@study ~]$ grep [-A] [-B] [--color=auto] '搜寻字串' filename
      选项与参数:

  • -A :后面可加数字,为 after 的意思,除了列出该行外,后续的 n 行也列出来;
  • -B :后面可加数字,为 befer 的意思,除了列出该行外,前面的 n 行也列出来;
  • -n 或 --line-number : 在显示符合样式的那一行之前,标示出该行的列数编号。
  • --color=auto 可将正确的那个撷取数据列出颜色

例如:dmesg 列出核心讯息,再以 grep 找出内含 eth 那行;要将捉到的关键字显色,且加上行号来表示;在关键字所在行的前两行与后三行也一起捉出来显示。

1.3 基础正则表达式练习

之后的练习大前提是:

  • 语系已经使用“ export LANG=C; export LC_ALL=C ”的设置值;
  • grep 已经使用 alias 设置成为“ grep --color=auto ”

如果 Linux 可以直接连上 Internet 的话,那么使用如下的指令来获取测试文件regular_express.txt即可:

wget http://linux.vbird.org/linux_basic/0330regularex/regular_express.txt

1.3.1 搜寻特定字串

要从刚刚的文件当中取得 the 这个特定字串,最简单的方式就是这样:

“反向选择”呢?也就是说,当该行没有 'the' 这个字串时才显示在屏幕上,那就直接使用:

1.3.2 利用中括号 [ ] 来搜寻集合字符

        [ ] 里面不论有几个字符,他都仅代表某“一个”字符。

如果我想要搜寻 testtaste 这两个单字时,可以发现到,其实她们有共通的 't?st' 存在~这个时候,我可以这样来搜寻:

上面的例子说明了,我需要的字串是“tast”或“test”两个字串而已! 而如果想要搜寻到 oo 前面不想要有小写字符,则使用:

1.3.3 行首与行尾字符 ^ $

(1)行首

想要让 the 只在行首列出,这个时候就得要使用定位字符^:

     

想要开头是小写字符的那一行就列出:

 ^ 符号,在字符集合符号(括号[ ])之内与之外是不同的! 在 [ ] 内代表“反向选择”,在 [ ] 之外则代表定位在行首的意义。

(2)行尾

特别注意到,因为小数点具有其他意义,所以必须要使用转义字符(\)来加以解除其特殊意义。

想要找出来行尾结束小数点 (.) 的那一行:

如果想要找出来,哪一行是“空白行”, 也就是说,该行并没有输入任何数据:

在一个程序脚本 (shell script) 或者是配置文件当中,空白行与开头为 # 的那一行是批注,因此如果你要将数据列出给别人参考时, 可以将这些数据省略掉以节省保贵的纸张,那么你可以怎么作呢? 我们以 /etc/rsyslog.conf 这个文件来作范例:

1.3.4 任意一个字符 . 与重复字符 *

bash 当中,我们知道 通配符 * 可以用来代表任意(0或多个)字符, 但是正则表达式并不是通配符,两者之间是不相同的! 至于正则表达式当中的“ . ”则代表“绝对有一个任意字符”的意思!这两个符号在正则表达式的意义如下:

  • . (小数点):代表“一定有一个任意字符”的意思;
  • *(星星号):代表“重复前一个字符, 0 到无穷多次”的意思,为组合形态。

假设需要找出 g??d 的字串,亦即共有四个字符, 起头是 g 而结束是 d ,可以这样做:

当需要“至少两个 o 以上的字串”时,就需要 ooo* ,亦即是:

 想要找出 g 开头与 g 结尾的字串,当中的字符可有可无,那该如何是好?

1.3.5 限定连续 RE 字符范围 {}

        在上个例题当中,我们可以利用 .RE 字符及 * 来设置 0 个到无限多个重复字符, 那如果我想要限制一个范围区间内的重复字符数呢?举例来说,我想要找出两个到五个 o 的连续字串,该如何作?这时候就得要使用到限定范围的字符 {} 了。 但因为 { } 的符号在 shell 是有特殊意义的,因此, 我们必须要使用转义字符 \ 来让他失去特殊意义才行。

假设我要找到两个 o 的字串,可以是:

我们要找出 g 后面接 2 到 5 个 o ,然后再接一个 g 的字串:

想要的是 2 个 o 以上的 goooo....g 呢?除了可以是 gooo*g ,也可以是:

1.4 基础正则表达式字符汇整 (characters)

经过了上面的几个简单的范例,我们可以将基础的正则表达式特殊字符汇整如下:

RE 字符意义与范例
^word意义:待搜寻的字串(word)在行首!范例:搜寻行首为 # 开始的那一行,并列出行号 > grep -n '^#' regular_express.txt
word$意义:待搜寻的字串(word)在行尾!范例:将行尾为 ! 的那一行打印出来,并列出行号 > 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\}意义:连续 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 字符”的意思~使用的意义并不相同,不要搞混了!

        举例来说,不支持正则表达式的 ls 这个工具中,若我们使用 “ls -l  代表的是任意文件名的文件,而 “ls -l a代表的是以 a 为开头的任何文件名的文件, 但在正则表达式中,我们要找到含有以 a 为开头的文件,则必须要这样:(需搭配支持正则表达式的工具)

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

ls -l | grep '^l

若仅想要列出几个文件,再以“ |wc -l ” 来累加处理即可。

注:

wc命令:

Linux wc命令用于计算字数。

利用wc指令我们可以计算文件的Byte数、字数、或是列数,若不指定文件名称、或是所给予的文件名为"-",则wc指令会从标准输入设备读取数据。

(1)语法:

wc [-clw][--help][--version][文件...]

(2)参数

  • -c或--bytes或--chars 只显示Bytes数。
  • -l或--lines 只显示行数。
  • -w或--words 只显示字数。
  • --help 在线帮助。
  • --version 显示版本信息。

(3)实例:

在默认的情况下,wc将计算指定文件的行数字数,以及字节数。使用的命令为:

    wc testfile 

1.5 sed 工具

     sed 本身也是一个管道命令,可以分析 standard input 。而且 sed 还可以将数据进行替换删除新增选取特定行等等的功能呢!

[dmtsai@study ~]$ 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 。

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

范例一:将 /etc/passwd 的内容列出并且打印行号,同时,请将第 2~5 行删除!
[dmtsai@study ~]$ nl /etc/passwd | sed '2,5d'
     1  root:x:0:0:root:/root:/bin/bash
     6  sync:x:5:0:sync:/sbin:/bin/sync
     7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
.....(后面省略).....

sed 的动作为 '2,5d' ,那个 d 就是删除!因为 2-5 行给他删除了,所以显示的数据就没有 2-5 行。 sed 后面接的动作,请务必以 '' 两个单引号括住!

举例来说,如果只要删除第 2 行,可以使用“ nl /etc/passwd | sed '2d' ”来达成, 至于若是要删除第 3 到最后一行,则是“ nl /etc/passwd | sed '3,$d' ”。

增加两行以上:

可以新增好几行,但是每一行之间都必须要以反斜线“ \ ”来进行新行的增加。

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

如果要整行取代呢?

示例:将第2-5行的内容取代成为“No 2-5 number”呢?
[dmtsai@study ~]$ nl /etc/passwd | sed '2,5c No 2-5 number'
     1  root:x:0:0:root:/root:/bin/bash
No 2-5 number
     6  sync:x:5:0:sync:/sbin:/bin/sync
.....(后面省略).....

我们以前想要列出第 11~20 行, 得要通过“head -n 20 | tail -n 10”之类的方法来处理,很麻烦啦。sed 则可以简单的直接取出你想要的那几行!是通过行号来找的!看看下面的范例先:

范例:仅列出 /etc/passwd 文件内的第 5-7 行
[dmtsai@study ~]$ nl /etc/passwd | sed -n '5,7p'
     5  lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
     6  sync:x:5:0:sync:/sbin:/bin/sync
     7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

上述的指令中有个重要的选项“ -n ”,按照说明文档,这个 -n 代表的是“安静模式”。没有加上 -n 的参数时,2-5行会重复输出。

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

        除了整行的处理模式之外, sed 还可以用行为单位进行部分数据的搜寻并取代的功能喔! 基本上 sed 的搜寻与取代的与 vi 相当的类似!

sed 's/要被取代的字串/新的字串/g'

        上表中特殊字体的部分为关键字,请记下来!至于三个斜线分成两栏就是新旧字串的替换! 

        取得 IP 数据的范例,先观察原始讯息,利用 /sbin/ifconfig  查询 IP ,利用关键字配合 grep 撷取出关键的一行数据,将 IP 前面的部分予以删除,将 IP 后面的部分予以删除:

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

        sed 甚至可以直接修改文件的内容!而不必使用管道命令数据流重导向! 不过,由于这个动作会直接修改到原始的文件,所以请你千万不要随便拿系统配置文件来测试。

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

[root@localhost ~]# sed -i 's/\.$/\!/g' regular_express.txt

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

[root@localhost ~]# sed -i '$a # This is a test' regular_express.txt 

2 扩展正则表达式

我们要去除空白行与行首为 # 的行列,使用的是

grep -v '^$' regular_express.txt | grep -v '^#'

需要使用到管线命令来搜寻两次!那么如果使用延伸型的正则表达式,我们可以简化为:

egrep -v '^$|^#' regular_express.txt

        grep 默认仅支持基础正则表达式,如果要使用延伸型正则表达式,你可以使用 grep -E , 不过更建议直接使用 egrep !直接区分指令比较好记忆!其实 egrep 与 grep -E 是类似命令别名的关系!

喔!

RE 字符意义与范例
+意义:重复“一个或一个以上”的前一个 RE 字符 范例:搜寻 (god) (good) (goood)... 等等的字串。 那个 o+ 代表“一个以上的 o ”所以,下面的执行成果会将第 1, 9, 13 行列出来。 > egrep -n 'go+d' regular_express.txt
?意义:“零个或一个”的前一个 RE 字符 范例:搜寻 (gd) (god) 这两个字串。 那个 o? 代表“空的或 1 个 o ”所以,上面的执行成果会将第 13, 14 行列出来。 有没有发现到,这两个案例( 'go+d' 与 'go?d' )的结果集合与 'go*d' 相同? 想想看,这是为什么喔! ^_^ > egrep -n 'go?d' regular_express.txt
|意义:用或( or )的方式找出数个字串 范例:搜寻 gd 或 good 这两个字串,注意,是“或”! 所以,第 1,9,14 这三行都可以被打印出来喔!那如果还想要找出 dog 呢? > egrep -n 'gd|good' regular_express.txt > egrep -n 'gd|good|dog' regular_express.txt
()意义:找出“群组”字串 范例:搜寻 (glad) 或 (good) 这两个字串,因为 g 与 d 是重复的,所以, 我就可以将 la 与 oo 列于 ( ) 当中,并以 | 来分隔开来,就可以啦! > egrep -n 'g(la|oo)d' regular_express.txt
()+意义:多个重复群组的判别 范例:将“AxyzxyzxyzxyzC”用 echo 叫出,然后再使用如下的方法搜寻一下! > echo 'AxyzxyzxyzxyzC' | egrep 'A(xyz)+C'

上面的例子意思是说,我要找开头是 A 结尾是 C ,中间有一个以上的 "xyz" 字串的意思~

以上这些就是延伸型的正则表达式的特殊字符。另外,要特别强调的是,那个 ! 在正则表达式当中并不是特殊字符, 所以,如果你想要查出来文件中含有 ! 与 > 的字行时,可以这样:

> grep -n '[!>]' regular_express.txt

3 文件的格式化与相关处理

3.1 格式化打印: printf

printf 可以帮我们将数据输出的结果格式化,而且而支持一些特殊的字符。

[dmtsai@study ~]$ printf '打印格式' 实际内容
选项与参数:
关于格式方面的几个特殊样式:

  •        \a    警告声音输出
  •        \b    倒退键(backspace)
  •        \f    清除屏幕 (form feed)
  •        \n    输出新的一行
  •        \r    亦即 Enter 按键
  •        \t    水平的 [tab] 按键
  •        \v    垂直的 [tab] 按键
  •        \xNN  NN 为两位数的数字,可以转换数字成为字符。

关于 C 程序语言内,常见的变量格式

  •        %ns   那个 n 是数字, s 代表 string ,亦即多少个字符;
  •        %ni   那个 n 是数字, i 代表 integer ,亦即多少整数码数;
  •        %N.nf 那个 n 与 N 都是数字, f 代表 floating (浮点),如果有小数码数,假设我共要十个位数,但小数点有两位,即为 %10.2f !%8.2f意义:00000.00
示例:关于第二行以后,分别以字串、整数、小数点来显示:
[dmtsai@study ~]$ printf '%10s %5i %5i %5i %8.2f \n' $(cat printf.txt | grep -v Name)
    DmTsai    80    60    92    77.33
     VBird    75    55    80    70.00
       Ken    60    90    70    73.33

3.2 awk:好用的数据处理工具

        awk 也是一个非常棒的数据处理工具!相较于 sed 常常作用于一整个行的处理, awk 则比较倾向于一行当中分成数个“字段”来处理。因此,awk 相当的适合处理小型的数据数据处理呢!awk 通常运行的模式是这样的:

[dmtsai@study ~]$ awk '条件类型1{动作1} 条件类型2{动作2} ...' filename

        awk 后面接两个单引号并加上大括号 {} 来设置想要对数据进行的处理动作。 awk 可以处理后续接的文件,也可以读取来自前个指令的 standard output 。 但如前面说的, awk 主要是处理“每一行的字段内的数据”,而默认的“字段的分隔符号为 "空白键" 或 "[tab]键" ”!

举例来说,想要取出帐号与登陆者的 IP ,且帐号与 IP 之间以 [tab] 隔开,结果如下所示:

[dmtsai@study ~]$ last -n 5 | awk '{print $1 "\t" $3}'
dmtsai  192.168.1.100
dmtsai  192.168.1.100
dmtsai  192.168.1.100
dmtsai  192.168.1.100
dmtsai  Fri

使用 awk 的时候,请先确认一下你的数据当中,如果是连续性的数据,请不要有空格或 [tab] 在内,否则,会发生误判!

在 awk 的括号内,每一行的每个字段都是有变量名称的,那就是 $1, $2... 等变量名称。以上面的例子来说, dmtsai 是 $1 ,因为他是第一栏嘛!至于 192.168.1.100 是第三栏, 所以他就是 $3 啦!后面以此类推~呵呵!还有个变量喔!那就是 $0 ,$0 代表“一整列数据”的意思~以上面的例子来说,第一行的 $0 代表的就是“dmtsai .... ”那一行啊! 由此可知,刚刚上面五行当中,整个 awk 的处理流程是:

  1. 读入第一行,并将第一行的数据填入 $0, $1, $2.... 等变量当中;
  2. 依据 "条件类型" 的限制,判断是否需要进行后面的 "动作";
  3. 做完所有的动作与条件类型;
  4. 若还有后续的“行”的数据,则重复上面 1~3 的步骤,直到所有的数据都读完为止。
变量名称代表意义
NF每一行 ($0) 拥有的字段总数
NR目前 awk 所处理的是“第几行”数据
FS目前的分隔字符,默认是空白键

 Tips 要注意喔,awk 后续的所有动作是以单引号“ ' ”括住的,由于单引号与双引号都必须是成对的, 所以, awk 的格式内容如果想要以 print 打印时,记得非变量的文字部分,包含上一小节 printf 提到的格式中,都需要使用双引号来定义出来喔!因为单引号已经是 awk 的指令固定用法了!

[dmtsai@study ~]$ last -n 5 | awk '{print $1 "\t lines: " NR "\t columns: " NF}'
dmtsai   lines: 1        columns: 10
dmtsai   lines: 2        columns: 10
dmtsai   lines: 3        columns: 10
dmtsai   lines: 4        columns: 10
dmtsai   lines: 5        columns: 9
# 注意喔,在 awk 内的 NR, NF 等变量要用大写,且不需要 $ 啦!

这样可以了解 NR 与 NF 的差别了吧?好了,下面来谈一谈所谓的 "条件类型" 了吧

  • awk 的逻辑运算字符

既然有需要用到 "条件" 的类别,自然就需要一些逻辑运算啰~例如下面这些:

运算单元代表意义
>大于
<小于
>=大于或等于
<=小于或等于
==等于
!=不等于

举例来说,在 /etc/passwd 当中是以冒号 ":" 来作为字段的分隔, 该文件中第一字段为帐号,第三字段则是 UID。那假设我要查阅,第三栏小于 10 以下的数据,并且仅列出帐号与第三栏。因为我们读入第一行的时候,那些变量 $1, $2... 默认还是以空白键为分隔的,所以虽然我们定义了 FS=":" 了, 但是却仅能在第二行后才开始生效。那么怎么办呢?我们可以预先设置 awk 的变量啊! 利用 BEGIN 这个关键字喔! 那么可以这样做:

而除了 BEGIN 之外,我们还有 END !另外,如果要用 awk 来进行“计算功能”呢?以下面的例子来看, 假设我有一个薪资数据表文件名为 pay.txt ,内容是这样的:

Name    1st     2nd     3th
VBird   23000   24000   25000
DMTsai  21000   20000   23000
Bird2   43000   42000   41000

如何计算每个人的总额呢?而且还想要格式化输出喔!我们可以这样考虑:

  • 第一行只是说明,所以第一行不要进行加总 (NR==1 时处理);
  • 第二行以后就会有加总的情况出现 (NR>=2 以后处理)
[dmtsai@study ~]$ cat pay.txt | \
< awk 'NR==1{printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total" }
NR>=2{total = $2 + $3 + $4
printf "%10s %10d %10d %10d %10.2f\n", $1, $2, $3, $4, total}'
      Name        1st        2nd        3th      Total
     VBird      23000      24000      25000   72000.00
    DMTsai      21000      20000      23000   64000.00
     Bird2      43000      42000      41000  126000.00

上面的例子有几个重要事项应该要先说明的:

  • awk 的指令间隔:所有 awk 的动作,亦即在 {} 内的动作,如果有需要多个指令辅助时,可利用分号“;”间隔, 或者直接以 [Enter] 按键来隔开每个指令,例如上面的范例中,鸟哥共按了三次 [enter] 喔!
  • 逻辑运算当中,如果是“等于”的情况,则务必使用两个等号“==”!
  • 格式化输出时,在 printf 的格式设置当中,务必加上 \n ,才能进行分行!
  • 与 bash shell 的变量不同,在 awk 当中,变量可以直接使用,不需加上 $ 符号。

利用 awk 这个玩意儿,就可以帮我们处理很多日常工作了!此外, awk 的输出格式当中,常常会以 printf 来辅助。另外, awk 的动作内 {} 也是支持 if (条件) 的喔! 举例来说,上面的指令可以修订成为这样:

[dmtsai@study ~]$ cat pay.txt | \
< awk '{if(NR==1) printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total"}
NR>=2{total = $2 + $3 + $4
printf "%10s %10d %10d %10d %10.2f\n", $1, $2, $3, $4, total}'

3.3 文件比对工具

3.3.1 diff

     diff 就是用在比对两个文件之间的差异的,并且是以行为单位来比对的!一般是用在 ASCII 纯文本文件的比对上。 由于是以行为比对的单位,因此 diff 通常是用在同一的文件(或软件)的新旧版本差异上!

[dmtsai@study ~]$ diff [-bBi] from-file to-file
选项与参数:

  • from-file :一个文件名,作为原始比对文件的文件名;
  • to-file   :一个文件名,作为目的比对文件的文件名;

注意,from-file 或 to-file 可以 - 取代,那个 - 代表“Standard input”之意。

  • -b  :忽略一行当中,仅有多个空白的差异(例如 "about me" 与 "about     me" 视为相同
  • -B  :忽略空白行的差异。
  • -i  :忽略大小写的不同。

3.3.2 cmp

        相对于 diff 的广泛用途, cmp 似乎就用的没有这么多了~ cmp 主要也是在比对两个文件,他主要利用“字节”单位去比对, 因此,当然也可以比对 binary file 啰~(还是要再提醒喔, diff 主要是以“行”为单位比对, cmp 则是以“字节”为单位去比对,这并不相同!)

[dmtsai@study ~]$ cmp [-l] file1 file2
选项与参数:
-l  :将所有的不同点的字节处都列出来。因为 cmp 默认仅会输出第一个发现的不同点。

范例一:用 cmp 比较一下 passwd.old 及 passwd.new
[dmtsai@study testpw]$ cmp passwd.old passwd.new
passwd.old passwd.new differ: char 106, line 4

3.3.3 patch

        diff 可以用来分辨两个版本之间的差异,如果要“升级”呢?就是“将旧的文件升级成为新的文件”时,应该要怎么做呢?就是“先比较先旧版本的差异,并将差异档制作成为补丁文件,再由补丁文件更新旧文件”即可。

        一般来说,使用 diff 制作出来的比较文件通常使用扩展名为 .patch 。至于内容就如同上面介绍的样子。 基本上就是以为单位,看看哪边有一样与不一样的,找到一样的地方,然后将不一样的地方取代掉! 以上面表格为例,新文件看到 - 会删除看到 + 会加入!好了,那么如何将旧的文件更新成为新的内容呢? 就是将 passwd.old 改成与 passwd.new 相同!可以这样做:

[dmtsai@study ~]$ patch -pN &lt; patch_file             <==更新
[dmtsai@study ~]$ patch -R -pN &lt; patch_file        <==还原

选项与参数:

  • -p  :后面可以接“取消几层目录”的意思。
  • -R  :代表还原,将新的文件还原成原来旧的版本。

3.4 文件打印准备: pr

如果想要打印 /etc/man_db.conf :

[dmtsai@study ~]$ pr /etc/man_db.conf

2014-06-10 05:35                 /etc/man_db.conf                 Page 1

#
#
# This file is used by the man-db package to configure the man and cat paths.
# It is also used to provide a manpath for those without one by examining
# configure script.
.....(以下省略)......

 

 

https://wizardforcel.gitbooks.io/vbird-linux-basic-4e/content/96.html

 

 

 

 

 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中,sed是一个非常强大的文本处理工具,可以与正则表达式相结合使用。sed中的正则表达式用于匹配和操作文本中的模式。 在sed中,正则表达式可以用于以下几种操作: 1. 匹配行:使用正则表达式匹配满足条件的行。 2. 替换文本:使用正则表达式匹配并替换文本。 3. 删除行:使用正则表达式匹配并删除满足条件的行。 4. 插入和追加文本:在满足条件的行之前或之后插入或追加文本。 5. 改变行:使用正则表达式修改满足条件的行。 在sed中,正则表达式使用特定的语法来表示不同的模式和操作。一些常用的正则表达式的语法包括: 1. 元字符:用于表示特殊的字符和字符类,如.表示任意字符,*表示前一个字符的零个或多个重复。 2. 字符类:用于匹配一组字符,如[a-z]表示小写字母,[0-9]表示数字。 3. 量词:用于指定字符或字符类的重复次数,如?表示零次或一次,+表示一次或多次,{n}表示精确重复n次。 4. 锚点:用于指定匹配的位置,如^表示行的开头,$表示行的结尾。 5. 分组和引用:用于将模式分组,并在替换中引用分组。 举个例子,假设我们有以下文本文件(luffycity.txt): His name is chaoge. I teach Linux. My linux is good. I like linux. I like linux very much. My telphone is 00000000 His qq is 88888888. His website is http://pythonav.cn. 如果我们想在每行下面插入"---",可以使用以下sed命令: sed 'a ---' luffycity.txt

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值