三大文本处理工具grep、sed及awk的简单介绍

grep、sed和awk都是文本处理工具,虽然都是文本处理工具单却都有各自的优缺点,一种文本处理命令是不能被另一个完全替换的,否则也不会出现三个文本处理命令了。只不过,相比较而言,sed和awk功能更强大而已,且已独立成一种语言来介绍。

grep:文本过滤器,如果仅仅是过滤文本,可使用grep,其效率要比其他的高很多;
sed:Stream EDitor,流编辑器,默认只处理模式空间,不处理原数据,如果你处理的数据是针对行进行处理的,可以使用sed;

awk:报告生成器,格式化以后显示。如果对处理的数据需要生成报告之类的信息,或者你处理的数据是按列进行处理的,最好使用awk。


grep
grep 是一个最初用于 Unix 操作系统的 命令行 工具。在给出文件列表或 标准输入 后,grep会对匹配一个或多个 正则表达式 的文本进行搜索,并只输出匹配(或者不匹配)的行或文本。 Unix的grep家族包括grep、egrep和 fgrep
egrep和 fgrep 的命令只跟grep有很小不同。egrep是grep的扩展,支持更多的re 元字符 fgrep 就是fixed grep或fast grep,它们把所有的字母都看作单词,也就是说, 正则表达式 中的元字符表示回其自身的字面意义,不再特殊。linux使用GNU版本的grep。它功能更强,可以通过-E、-F命令行选项来使用egrep和 fgrep 的功能。
grep的工作方式是这样的,它在一个或多个文件中搜索字符串模板。如果模板包括空格,则必须被引用,模板后的所有字符串被看作文件名。搜索的结果被送到屏幕,不影响原文件内容。
grep可用于shell 脚本 ,因为grep通过返回一个状态值来说明搜索的状态,如果模板搜索成功,则返回0,如果搜索不成功,则返回1,如果搜索的文件不存在,则返回2。我们利用这些返回值就可进行一些自动化的文本处理工作。

grep:根据模式搜索文本,并将符合模式的文本行显示出来。    
Pattern:文本字符和正则表达式的元字符组合而成匹配条件

使用格式:
grep [options] PATTERN [FILE...]
    -i:忽略大小写    
    --color:匹配到字符用其他颜色显示出来,默认是红色
    -v:显示没有被模式匹配到的行
    -o:只显示被模式匹配到的字符串,不显示行
    -E:使用扩展正则表达式
    -A n:表示显示该行及其后n行
    -B n:表示显示该行及其前n行
    -C n:表示显示该行及其前后各n行
正则表达式:REGular EXPression,REGEXP
元字符:
.:匹配任意单个字符
[]:匹配指定范围内的任意单个字符
[^]:匹配指定范围外的任意单个字符
   字符集和:[:digit:],[:lower:],[:upper:],[:punct:],[:space:],[:alpha:],[:alnum:]
    对应上边:数字  ,小写字母,大写字母,标点符号,空白字符,所有字母,所有数字和字母

匹配次数(贪婪模式,即尽可能长的匹配):
*:匹配其前面的字符任意次 
   如:编辑文件abc,输入这些字符:a,b,ab,aab,acb,adb,amnb,使用grep匹配a*b,命令及显示效果如下所示:
从上图可以看到,虽然像acb、adb之类的字符串也可以显示出来,但从显示的颜色可以看到,匹配的仅仅是字符b,虽然整个字符串中也有字符a,但是此时a并没有与b一起紧挨着出现,所以仅匹配到字符b;而像ab、aab等字符串,全部显示,此时a紧挨着b出现,符合条件,所以可以显示。

.*:任意长度的任意字符
    如:匹配.b和.*b,看二者有什么区别,命 令和显示效果如下:


\?:匹配其前面的字符1次或0次

    如:继续上边的例子,现在匹配a\?b,显示效果如下所示:


\{m,n\}:匹配其前面的字符至少m次,至多n次

   \{1,\}:至少一次
   \{0,3\}:最多三次

位置锚定:
^:锚定行首,此字符后面的任意内容必须出现在行首    
$:锚定行尾,此字符前面的任意内容必须出现在行尾
^$:空白行
\<或\b:锚定词首,其后面的任意字符必须作为单词首部出现
\>或\b:锚定词尾,其后边的任意字符必须作为单词尾部出现

    如:\<root\>:在整个文件中,把root作为整个单词出现,词首词尾都不行,如:mroot、rooter均将不匹配
举几个例子来说明下上边的位置锚定。


分组:

\(\)
   如: \(ab\)*:ab整体作为匹配字符,且匹配任意次
          \(ab\)\{1,\}:ab整体作为匹配字符,且匹配至少一次
          \(ab\):ab整体作为匹配字符

    后向引用
    \1:匹配第一个左括号以及与之对应的右括号所包括的所有内容
    \2:匹配第二个左括号以及与之对应的右括号所包括的所有内容
    \3:匹配第三个左括号以及与之对应的右括号所包括的所有内容
    ......
    如:
新建文件test1,添加内容如下:
He love his lover.
She like her liker.
He love his liker.
She like her lover.
He like her.
She love he.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vim test1      #编辑该文件添加如下内容:
He love his lover.
She like her liker.
He love his liker.
She like her lover.
He like her.
She love he.
[root@www ~] # grep '\(l..e\).*\1' test1    #匹配前边第一个左括号的以及与之对应的右括号的内容
He love his lover.
She like her liker.
[root@www ~] # grep 'l..e' test1          #匹配指定的字符,中间可为任意两个字符
He love his lover.
She like her liker.
He love his liker.
She like her lover.
He like her.
She love he.


grep练习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1、显示 /proc/meminfo 文件中以不区分大小的s开头的行;
      grep  -i  '^s'  /proc/meminfo     或者
      grep  '^[sS]'  /proc/meminfo    #[]表示匹配指定范围内的单个字符,因此也可实现不区分大小写
2、显示 /etc/passwd 中以nologin结尾的行;
      grep  'nologin$'  /etc/passwd
      扩展一:取出默认shell为 /sbin/nologin 的用户列表
            grep  '/sbin/nologin'  /etc/passwd  cut  -d: -f1    或者
            grep  '/sbin/nologin'  /etc/passwd  awk  -F:  '{print $1}'      或者直接使用 awk
            awk  -F:  '$7 ~ /nologin/{print $1}'  /etc/passwd
      扩展二:取出默认shell为 bash ,且其用户ID号最小的用户的用户名
            grep  'bash$'  /etc/passwd  sort  -n -t: -k3 |  head  -1 |  cut  -d: -f1   或者
            awk  -F:  '$7 ~ /bash/{print $3,$1}'  /etc/passwd  sort  -n |  head  -1 |  awk  '{print $2}'
3、显示 /etc/inittab 中以 #开头,且后面跟一个或多个空白字符,而后又跟了任意非空白字符的行;
       grep  '^#[[:space:]]\{1,\}[^[:space:]]'  /etc/inittab
4、显示 /etc/inittab 中包含了:一个数字:(即两个冒号中间一个数字)的行;
       grep  ':[0-9]:'  /etc/inittab
5、显示 /boot/grub/grub .conf文件中以一个或多个空白字符开头的行;
       grep  '^[[:space:]]\{1,\}'  /boot/grub/grub .conf
6、显示 /etc/inittab 文件中以一个数字开头并以一个与开头数字相同的数字结尾的行;
       grep  '\(^[0-9]\).*\1$'  /etc/inittab      #在RHEL5.8以前的版本中可查看到效果
7、找出某文件中的,1位数,或2位数;
       grep  '\<[[:digit:]][[:digit:]]\?\>'  /etc/inittab   或者
       grep  '\<[0-9]\{1,2\}\>'  /etc/inittab
8、查找当前系统上名字为student(必须出现在行首)的用户的帐号的相关信息, 文件为 /etc/passwd
       grep  '^student:'  /etc/passwd
       扩展:若存在该用户,找出该用户的ID号:
             grep  '^student:'  /etc/passwd  cut  -d: -f3  或者 # id -u student
思考题:用多种方法找出本地的IP地址,这里给出三种方法,如果你还有其他方法可以一起分享下:
1
2
3
ifconfig  eth0| grep  -oE  '([0-9]{1,3}\.?){4}' | head  -n 1
ifconfig  eth0| awk  -F:  '/inet addr/{split($2,a," ");print a[1];exit}'      #这里使用了awk的内置函数,如果不懂可在看完awk的介绍后再来做此题
ifconfig  | grep  "inet addr" | grep  - v  "127.0.0.1"  | awk  -F:  '{print $2}'  | awk  '{print $1}'


sed
sed (意为 流编辑器 ,源自 英语 stream editor ”的缩写)是 Unix 常见的 命令行 程序。sed 用来把文档或 字符串 里面的文字经过一系列编辑命令转换为另一种格式输出。sed 通常用来匹配一个或多个 正则表达式 的文本进行处理。 sed是一种在线 编辑器 ,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向 存储 输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

使用格式:    
sed 'AddressCommand' file ...
    -n:静默模式,不再显示模式空间中的内容
    -i:直接修改原文件
    -e SCRIPT -e SCRIPT:添加脚本到将要执行的命令中,可以同时执行多个脚本
    -f /PATH/TO/SED_SCORIPT:添加脚本文件中的内容到将要执行的命令中
         #sed -f /path/to/script file
    -r:表示使用扩展正则表达式

Address:
1、StartLine,EndLine:开始行,结束行
   如:1,100:表示从第1行到第100行
    $;最后一行
2、/RegExp/:扩展正则表达式
   如:/^root/
3、/pattern1/,/pattern2/:表示第一次被pattern1匹配到的行开始,至第一次被pattern2匹配到的行结束,这中间的所有行
4、LIneNumber:指定的行
5、StartLIne,+N:从StartLine开始,向后的N行

Command:
    d:删除符合条件的行;
    p:显示符合条件的行,在不使用-n选项时被匹配到的行会显示两遍,因为sed处理时会把处理的信息输出
    a \string:在指定的行后面追加新行,内容为“string”,
        显示两行或多行,在每行后加\n进行换行
    i \string:在指定的行前面添加新行,内容为string
    r file:将指定的文件的内容添加至符合条件的文件中
    w file:将地址指定范围内的行另存至指定的文件中
    s/pattern/string/修饰符:查找并替换,默认只替换每行中第一次被模式匹配到的字符串
        加修饰符
         g:全局替换,如:s/pattern/string/g
         i:忽略字符大小写,如:s/pattern/string/i
    s///,s###,s@@@都可以,当所使用的分割符号与内容中显示的相同时,需使用转义字符转义    
        \(\),\1,\2:成组匹配,\1表示匹配第一个‘(’,\2表示匹配第二个‘(’
    &:引用模式匹配到的整个串
介绍了那么多,现在从我们的系统中复制个文件,作为我们的测试文件,这里,我们复制/etc/inittab文件,并稍加修改,删掉几行,然后作为测试文件使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cp  /etc/inittab  ./     #复制该文件到当前目录下,这里当前处于root家目录下。
vim inittab      #编辑该文件,修改此文件,内容如下所示,其内容也可参看下边的图片
# inittab is only used by upstart for the default runlevel.
#
           #此处是一空行,没有任何字符,就光空行添加代码时会被删掉,所以这里加上备注,说明是一空交行,下面相同
# Individual runlevels are started by /etc/init/rc.conf
#
            #此处是一空行,没有任何字符。该文件内容也看参见下边的图片
# Default runlevel. The runlevels used are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
id :3:initdefault:



用复制并修过的inittab文件作为测试文件,举几个例子:

1
[root@www ~] # sed '1,3d' inittab:表示删除文件的第一到第三行


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
[root@www ~] # sed '3,$d' inittab   #表示删除模式空间中的第三到最后一行
# inittab is only used by upstart for the default runlevel.
#
[root@www ~] # sed '/run/d' inittab     #表示删除被run匹配到的行
#
#
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
id :3:initdefault:
[root@www ~] # sed '/^#/d' inittab     #表示删除文件中以#开头的行
id :3:initdefault:
[root@www ~] # sed -n '/^\//p' inittab    #显示以/开头的行,因为没有这样的行,故不显示任何信息
[root@www ~] # sed '/^#/p' inittab        #显示以#开头的行,可以看到被匹配到的行,均显示了两遍,这是因为sed处理时会把处理的信息输出
# inittab is only used by upstart for the default runlevel.
# inittab is only used by upstart for the default runlevel.
#
#
# Individual runlevels are started by /etc/init/rc.conf
# Individual runlevels are started by /etc/init/rc.conf
#
#
# Default runlevel. The runlevels used are:
# Default runlevel. The runlevels used are:
#   0 - halt (Do NOT set initdefault to this)
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   3 - Full multiuser mode
#   4 - unused
#   4 - unused
#   5 - X11
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#   6 - reboot (Do NOT set initdefault to this)
#
#
id :3:initdefault:
[root@www ~] # sed -n '/^#/p' inittab     #显示以#开头的行,使用-n选项表示仅显示匹配到的行
# inittab is only used by upstart for the default runlevel.
#
# Individual runlevels are started by /etc/init/rc.conf
#
# Default runlevel. The runlevels used are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#


为了后边的演示,创建文件test,添加如下内容:
Welcome to my linux!
This is my world.
How are you?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
vim  test     #编辑该文件添加如下内容:
Welcome to my linux!
This is my world.
How are you?
[root@www ~] # sed '2r ./test' inittab    #表示将test文件中的内容添加到inittab文件中,且从第二行往后开始添加
# inittab is only used by upstart for the default runlevel.
#
Welcome to my linux!               #新添加的三行内容
This is my world.
How are you?
# Individual runlevels are started by /etc/init/rc.conf
#
# Default runlevel. The runlevels used are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
id :3:initdefault:
[root@www ~] # sed '1,2r ./test' inittab    #表示将test文件中的内容添加到inittab文件中,且分别 添加在第一和第二行后边
# inittab is only used by upstart for the default runlevel.
Welcome to my linux!               #新添加的三行内容
This is my world.
How are you?
#
Welcome to my linux!                     #新添加的三行内容
This is my world.
How are you?
# Individual runlevels are started by /etc/init/rc.conf
#
# Default runlevel. The runlevels used are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
id :3:initdefault:
[root@www ~] # sed 's/linux/LINUX/g' test     #查找该文件中的字符串,然后替换为指定的字符串
Welcome to my LINUX!                 #可以看到以替换为大写的linux
This is my world.
How are you?
[root@www ~] # sed 's/y.u/&r/g' test    #查找指定的字符串,并将其替换为在其后加上r,分隔符采用/
Welcome to my linux!
This is my world.
How are your?
[root@www ~] # sed 's@y.u@&r@g' test     #意义同上,分隔符采用@
Welcome to my linux!
This is my world.
How are your?
[root@www ~] # sed 's#y.u#&r#g' test      #意义同上,分隔符采用#
Welcome to my linux!
This is my world.
How are your?


sed练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1、删除 /etc/grub .conf文件中行首的空白符;
       sed   -r  's/^[[:space:]]+//'  /etc/grub .conf
2、替换 /etc/inittab 文件中“ id :3:initdefault:”一行中的数字为5;
      sed  's/\(id:\)[0-9]\(:initdefault:\)/\15\2/g'  /etc/inittab
3、删除 /etc/inittab 文件中的空白行;
      sed  '/^$/d'  /etc/inittab
4、删除 /etc/inittab 文件中开头的 #号;
      sed  's/^#//g'  /etc/inittab
5、删除某文件中开头的 #号及其后面的空白字符,但要求#号后面必须有空白符;
      sed  's/^#[[:space:]]\{1,\}//g'  /etc/inittab      或者
      sed   -r  's/^#[[:space:]]+//g'  /etc/inittab
6、删除某文件中以空白字符后面跟 #类的行中的开头的空白字符及#
      sed  -r  's/^[[:space:]]+#//g'  /etc/inittab
7、取出一个文件路径的目录名称;
      echo  "/etc/rc.d/abc/edu/"  sed  -r  's@^(/.*/)[^/]+/?@\1@g'         #因sed支持扩展正则表达式,在扩展正则表达式中,+表示匹配其前面的字符至少1次
8、取出一个文件路径的最后一个文件名;
      echo  "/etc/rc.d/abc/edu/"  sed  -r  's@^/.*/([^/]+)/?@\1@g'

awk

awk是一种优良的文本处理工具,LinuxUnix环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称得自于它的创始人Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母)的最大功能取决于一个人所拥有的知识。AWK提供了极其强大的功能:可以进行正则表达式的匹配,样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。它具备了一个完整的语言所应具有的几乎所有精美特性。实际上AWK的确拥有自己的语言:AWK程序设计语言,三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。最简单地说,AWK是一种用于处理文本的编程语言工具。我们现在使用最多的是gawk,gawk是AWK的GNU版本。

使用格式:
# awk [options] 'script' file1, file2, ...
# awk [options] 'PATTERN { action }' file1, file2, ...

awk的输出:print和printf
一、print
print的使用格式:
    print item1, item2, ...
要点:
1、各项目之间使用逗号隔开,而输出时则以空白字符分隔;
2、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;
3、print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print "";

例子:
1
2
awk  'BEGIN { print "line one\nline two\nline three" }'
awk  -F:  '{ print $1, $7 }'  /etc/passwd      #等价于:awk -v FS=: '{print $1,$7}' /etc/passwd


二、awk变量

2.1 awk内置变量之记录变量:
FS: field separator,字段分隔符,默认是空白字符;
RS: Record separator,记录分隔符,默认是换行符;
OFS: Output Filed Separator,输出字段分隔符
ORS:Output Row Separator,输出行分隔符

一起来看一个示例:

1
2
3
4
5
vim  test .txt    #编辑该文件,添加如下两行信息作为示例使用
welcome to redhat linux.
how are you?
[root@www ~] # awk 'BEGIN{OFS="#"} {print $1,$2}' test.txt    #指定输出时的分隔符
[root@www ~] # awk 'BEGIN{OFS="#"} {print $1,"hello",$2}' test.txt   #指定输出时的分隔符,并添加显示的内容
上边两个命令的显示效果如下所示:

1
2
3
4
5
6
7
8
9
[root@www ~] # awk -v FS=:  -v OFS=# '{print $1,$7}' /etc/passwd    #以:为字段分隔符,以#号为输出分隔符,显示该文件的第一及第七字段的值,这里仅贴出部分显示内容
root #/bin/bash
bin #/sbin/nologin
daemon #/sbin/nologin
adm #/sbin/nologin
lp #/sbin/nologin
sync #/bin/sync
shutdown #/sbin/shutdown
halt #/sbin/halt
2.2 awk内置变量之数据变量:
NR: The number of input records,awk命令所处理的记录数;如果有多个文件,这个数目会把处理的多个文件中行统一计数;
NF:Number of Field,当前记录的字段个数,有时可用来表示最后一个字段
FNR: 与NR不同的是,FNR用于记录正处理的行是当前这一文件中被总共处理的行数;
ARGV: 数组,保存命令行本身这个字符串,如awk '{print $0}' a.txt b.txt这个命令中,ARGV[0]保存awk,ARGV[1]保存a.txt;
ARGC: awk命令的参数的个数;
FILENAME: awk命令所处理的文件的名称;
ENVIRON:当前shell环境变量及其值的关联数组;
如:
1
2
awk  'BEGIN{print ENVIRON["PATH"]}'
awk  '{print $NF}'  test .txt

2.3 用户自定义变量
gawk允许用户自定义自己的变量以便在程序代码中使用,变量名命名规则与大多数编程语言相同,只能使用字母、数字和下划线,且不能以数字开头。gawk变量名称区分字符大小写。

2.3.1 在脚本中赋值变量
在gawk中给变量赋值使用赋值语句进行,例如:
1
2
[root@www ~] # awk 'BEGIN{var="variable testing";print var}'   #给变量赋值,并输出变量的值,下边是显示效果
variable testing

2.3.2 在命令行中使用赋值变量
gawk命令也可以在“脚本”外为变量赋值,并在脚本中进行引用。例如,上述的例子还可以改写为:
1
2
[root@www ~] # awk -v var="variable testing" 'BEGIN{print var}'     #与上述的例子一样,显示效果如下
variable testing

三、printf
printf命令的使用格式:
printf format, item1, item2, ...
要点:
1、其与print命令的最大不同是,printf需要指定格式;
2、format用于指定后面的每个item的输出格式;
3、printf语句不会自动打印换行,需要显式使用\n换行。

format格式的指示符都以%开头,后跟一个字符;如下:
%c: 显示字符的ASCII码;
%d, %i:十进制整数;
%e, %E:科学计数法显示数值;
%f: 显示浮点数;
%g, %G: 以科学计数法的格式或浮点数的格式显示数值;
%s: 显示字符串;
%u: 无符号整数;
%%: 显示%自身;

修饰符:
N: 显示宽度;
-: 左对齐;
+:显示数值符号;
例子:
1
awk  -F:  '{printf "%-15s%i\n",$1,$3}'  /etc/passwd       #使用printf显示该文件中的第一列和第三列,要求第一列左对齐且占用15个字符宽度,第二列显示十进制整数,显示效果如下所示:


四、输出重定向

使用格式:
print items > output-file
print items >> output-file
print items | command
特殊文件描述符:
/dev/stdin:标准输入
/dev/sdtout: 标准输出
/dev/stderr: 错误输出
/dev/fd/N: 某特定文件描述符,如/dev/stdin就相当于/dev/fd/0;
例子:
1
awk  -F:  '{printf "%-15s %i\n",$1,$3 > "/dev/stderr" }'  /etc/passwd     #显示效果与上述例子一样,只不过这里是重定向到错误输出,然后显示

六、awk的操作符:
6.1 算术操作符:
-x:负值
+x:转换为数值;
x^y:x的y次方
x**y: x的y次方
x*y:乘法
x/y:除法
x+y:加法
x-y:减法
x%y:取余

6.2 字符串操作符:
只有一个,而且不用写出来,用于实现字符串连接;
1
2
[root@www ~] # awk 'BEGIN{print "A" "B"}'     #连接A和B两个字符,使其成为一个字符串,显示效果如下所示:
AB

6.3 赋值操作符:
=
+=
-=
*=
/=
%=
^=
**=
++
--

需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代;

6.4 布尔值
awk中,任何非0值或非空字符串都为真,反之就为假;

6.5 比较操作符:
x < y     True if x is less than y.
x <= y     True if x is less than or equal to y.
x > y     True if x is greater than y.
x >= y     True if x is greater than or equal to y.
x == y     True if x is equal to y.
x != y     True if x is not equal to y.
x ~ y     True if the string x matches the regexp denoted by y. 如果字符串x被y表示的正则表达式匹配,则为真;
x !~ y     True if the string x does not match the regexp denoted by y. 如果字符串x不被y表示的正则表达式匹配,则为真;
subscript in array       True if the array array has an element with the subscript subscript.

如:
1
2
3
[root@www ~] # awk -F: '$1 ~ /^root/{print $1,$3,$4,$NF}' /etc/passwd    #显示该文件中以root开头的行的第一列、第三列、第四列和最后一列,显示效果如下所示:
root 0 0  /bin/bash
[root@www ~] # awk -F: '$3>=400{printf "%-15s%-10i%s\n",$1,$3,$NF}' /etc/passwd #显示UID大于400的行的第一列、第三列和最后一列

6.7 表达式间的逻辑关系符:
&&
||

6.8 条件表达式:
selector?if-true-exp:if-false-exp

6.9 函数调用:
function_name (para1,para2)

七 awk的模式:
awk 'program' input-file1 input-file2 ...
其中的program为:
pattern { action }
pattern { action }
...

7.1 常见的pattern类型:
1、Regexp: 正则表达式,格式为/regular expression/
    如:
1
2
3
4
5
[root@www ~] # awk -F: '/bash/{print $0}' /etc/passwd    #在/etc/passwd中查找有bash的行,并显示
root:x:0:0:root: /root : /bin/bash
nginx:x:496:493:: /home/nginx : /bin/bash
mysql:x:495:492:: /home/mysql : /bin/bash
wpuser:x:494:491:: /home/wpuser : /bin/bash

2、expresssion: 表达式,其值非0或为非空字符时满足条件,如:$1 ~ /foo/ 或 $1 == "lq2419",用运算符~(匹配)和~!(不匹配)。
    如:
1
2
3
awk  -F:  '$3>=500{printf "%-15s%s\n",$1,$3}'  /etc/passwd
awk  -F:  '$1 ~ /root/{print $1,$3}'  /etc/passwd
      等价于: awk  -F:  '$1=="root"{print $1,$3}'  /etc/passwd

3、Ranges: 指定的匹配范围,格式为pat1,pat2

4、BEGIN/END:特殊模式,仅在awk命令执行前运行一次或结束前运行一次
    如:
1
2
3
4
5
6
7
8
9
[root@www ~] # awk -F: 'BEGIN{print "Username       UID"}$3>=400{printf "%-15s%s\n",$1,$3}' /etc/passwd       #上式表示对于该文件,在开始比较前先输出username和UID作为题头,然后,输出UID大于400的行的第一列和第三列
Username       UID
rtkit          499
pulse          498
saslauth       497
nfsnobody      65534
nginx          496
mysql          495
wpuser         494
现在我想输出/etc/passwd文件中的第一列,那么,有多少种方法可以实现呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
有三中方式均可以实现,命令及显示效果如下:
[root@www ~] #  awk -v FS=: '{print $1}' /etc/passwd    #使用-v选项和FS指定分隔符,然后显示
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
uucp
[root@www ~] # awk -F: '{print $1}' /etc/passwd       #直接使用-F选项指定分隔符,然后显示
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
uucp
[root@www ~] # awk 'BEGIN{FS=":"}{print $1}' /etc/passwd    #使用BEGIN,在运行前指定分隔符,然后显示
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
uucp

5、Empty(空模式):匹配任意输入行;

7.2 常见的Action
1、Expressions:
2、Control statements
3、Compound statements
4、Input statements
5、Output statements


/正则表达式/:使用通配符的扩展集。
关系表达式:可以用下面运算符表中的关系运算符进行操作,可以是字符串或数字的比较,如$2>$1选择第二个字段比第一个字段长的行。

模式匹配表达式:
模式,模式:指定一个行的范围。该语法不能包括BEGIN和END模式。
BEGIN:让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。
END:让用户在最后一条输入记录被读取之后发生的动作。

八 控制语句:
8.1 if-else
语法:if (condition) {then-body} else {[ else-body ]}
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@www ~] # awk -F: '{if ($1=="root") print $1, "Admin"; else print $1, "Common User"}' /etc/passwd    #判断如果第一个字段是root,则显示是admin,否则显示是common user,显示结果如下所示:
root Admin
bin Common User
daemon Common User
adm Common User
lp Common User
sync  Common User
shutdown  Common User
halt Common User
[root@www ~] # awk -F: '{if ($1=="root") printf "%-15s%s\n", $1,"Admin"; else printf "%-15s%s\n", $1, "Common User"}' /etc/passwd          #显示效果同上,只不过这里使用printf,显示定义显示的格式
root             Admin
bin              Common User
daemon           Common User
adm              Common User
lp               Common User
sync              Common User
shutdown          Common User
halt             Common User
mail             Common User
uucp             Common User
[root@www ~] # awk -F: -v sum=0 '{if ($3>=400) sum++}{if ($3>=400) printf "%-15s%i\n",$1,$3}END{print "Sum = "sum;}' /etc/passwd                #定义变量sum=0,然后判断如果UID大于400,让sum自加,并且如果UID大于等于400的显示其用户名和UID,结束前输出sum的值
rtkit             499
pulse             498
saslauth          497
nfsnobody         65534
nginx             496
mysql             495
wpuser            494
Sum = 7

8.2 while
语法: while (condition){statement1; statment2; ...}
如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@www ~] # awk -F: -v i=1 '{while (i<=7) {print $i;i++}}' /etc/passwd    #使用-v选项显示定义变量i=1,当i小于等于7时循环结束,然后,输出第一个字段到第七个字段,当i为7时,正好输出的是第一行的7个字段的值
root
x
0
0
root
/root
/bin/bash
[root@www ~] # awk -F: '{i=1;while (i<=3) {print $i;i++}}' /etc/passwd    #定义变量i,先判断,当i小于等于3时,输出第一到第三个字段的值,所处理的数据时该文件中的每一行数据,而不仅仅是第一行。这里需要注意与上述命令的区别
root
x
0
bin
x
1
daemon
x
2
adm
x
3
lp
x
4

8.3 do-while
语法: do {statement1, statement2, ...} while (condition)
如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@www ~] # awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd    #意义同上,显示效果虽与上述相同,但与while不同的是,do-while是先执行一趟,然后再判断是否满足条件,也就是说不管条件是否满足,都会先执行一趟;而while中如果条件不满足,则一趟也不会执行
root
x
0
bin
x
1
daemon
x
2
adm
x
3
lp
x
4

8.4 for
语法: for ( variable assignment; condition; iteration process) { statement1, statement2, ...}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@www ~] # awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd     #使用for循环,输出各行的前三个字段
root
x
0
bin
x
1
daemon
x
2
adm
x
3
lp
x
4

for循环还可以用来遍历数组元素:
语法: for (i in array) {statement1, statement2, ...}
1
2
3
4
5
6
[root@www ~] # awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd      #匹配最后一个字段不空的行,把最后一段当做数组下标,输出各下标的值,及各下标对应的个数,各下标的个数保存在数组中
          /bin/sync :1
          /bin/bash :4
      /sbin/nologin :29
         /sbin/halt :1
     /sbin/shutdown :1


8.5 case
语法:switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}

8.6 break 和 continue
常用于循环或case语句中

8.7 next
提前结束对本行文本的处理,并接着处理下一行;例如,下面的命令将显示其ID号为奇数的用户:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@www ~] # awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd    #UID号对2取余,如果为0,直接处理下一行,否则,输出用户名和UID号,显示效果如下所示:
bin 1
adm 3
sync  5
halt 7
operator 11
gopher 13
nobody 99
dbus 81
usbmuxd 113
vcsa 69
rtkit 499
saslauth 497
postfix 89
abrt 173
rpcuser 29
mysql 495

九 awk中使用数组
9.1 数组
array[index-expression]
index-expression可以使用任意字符串;需要注意的是,如果某数据组元素事先不存在,那么在引用其时,awk会自动创建此元素并初始化为空串;因此,要判断某数据组中是否存在某元素,需要使用index in array的方式。

要遍历数组中的每一个元素,需要使用如下的特殊结构:
for (var in array) { statement1, ... }
其中,var用于引用数组下标,而不是元素值;
例子:
1
2
netstat  -ant |  awk  '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
每出现一被/^tcp/模式匹配到的行,数组S[$NF]就加1,NF为当前匹配到的行的最后一个字段,此处用其值做为数组S的元素索引
1
2
3
4
5
6
7
8
9
10
11
[root@www ~] # awk '{IP[$1]++}END{for (A in IP) printf "%-20s%s\n",A,IP[A]}' /var/log/nginx/access.log     #用法与上一个例子相同,用于统计某日志文件中IP地的访问量
172.16.32.30         3
172.16.32.50         31596
172.16.32.52         408
192.168.0.239        1886
172.16.32.0          1010
[root@www ~] # awk 'BEGIN{A["m"]="hello";A["n"]="world";print A["m"],A["n"]}'     #开始前对数组赋值,然后输出数组的值
hello world
[root@www ~] # awk 'BEGIN{A["m"]="hello";A["n"]="world";for (B in A) print A[B]}'      #在开始前对数组赋值,然后使用for循环,把B当做下标,依次输出数组中的值
hello
world

9.2 删除数组变量
从关系数组中删除数组索引需要使用delete命令。使用格式为:
delete  array[index]

十、awk的内置函数
split(string, array [, fieldsep [, seps ] ])
功能:将string表示的字符串以fieldsep为分隔符进行分隔,并将分隔后的结果保存至array为名的数组中;数组下标为从0开始的序列;
1
netstat  -ant |  awk  '/:22\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}'  sort  -rn |  head  -50   #显示效果如下所示:
length([string])
功能:返回string字符串中字符的个数;
substr(string, start [, length])
功能:取string字符串中的子串,从start开始,取length个;start从1开始计数;
system(command)
功能:执行系统command并将结果返回至awk命令
systime()
功能:取系统当前时间
tolower(s)
功能:将s中的所有字母转为小写
toupper(s)
功能:将s中的所有字母转为大写

十一、用户自定义函数
自定义函数使用function关键字。格式如下:
function F_NAME([variable])
{
    statements
}

函数还可以使用return语句返回值,格式为“return value”。


在最后,介绍个面试中常被问到的关于awk的一个问题,顺便给出几个好玩的awk命令。有时候面试的时候,会被问到,使用awk编写脚本打印出乘法口诀,牛人当然觉得这小菜一碟,但是,像我们这些菜鸟,可就该抓耳挠腮了,本人经过上网搜索,加上自己尝试,写出了该脚本。先附上脚本及打印效果,以供参考。
1、乘法口诀
1
2
3
4
5
6
7
8
9
10
11
12
vim cfkj.sh    #编辑该文件,输入如下内容:
#!/bin/awk -f
BEGIN{
         for  (i=1;i<=9;i++)
         {
         for  (m=1;m<=i;m++)
                 {
                         printf  m "*" i "=" m*i "  "
                 }
         print
         }
}


2、打印图案

接触过C语言的都知道,我们在刚开始学C语言的时候,有时候为了让你能够学下去,特别是在谭浩强版的C程序设计丛书中,会有很多课后习题,让你用C语言实现打印一些图案等,甚至是一些有意思的像编程实现使各行、各列以及对角线之和都相等之类的图标等等。那如果现在让你用awk来实现,打印一个图案,又该如何实现呢?比方说,使用awk打印如下图案。
         *
        ***
      *****
     *******
对于这个我们又该如何实现呢?其实,可使用如下命令实现打印此图案。
1
awk  'BEGIN {for(i=1;i<=4;i++) {for(j=1;j<=10-i;j++) {printf " ";}for(j=1;j<=(2*i-1);j++) {printf "*";}printf "\n";}}'

3、使用数字打印中心对称图形
这个就有点难度了。本人也没有想出来,这个是一个朋友给我的,觉得好玩,就拿出来跟大家分享一下。这里直接给出命令及显示效果了,就不吊各位胃口了。
1
2
echo  15| awk  '{x=8; for (i=1;i<$0;i++){ for (j=1;j<=3*($0-i)-(x>0?x:0);j++) printf " " ; for (k=i;k>=1;k--) printf "%d " ,k; for (l=2;l<=i;l++) printf "%d " ,l; printf "\n" ;x--};\
for (i=1;i<=$0;i++){ for (j=1;j<=(i<=$0-10+1?3*(i-1):3*(i-1)+2-(i-$0%10-10*int(($0-10) /10 )));j++) printf " " ; for (k=$0-i+1;k>=1;k--) printf "%d " ,k; for (l=2;l<=$0-i+1;l++) printf "%d " ,l; printf "\n" }}'
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值