Linux awk命令详解

awk :适用程序,一种unix工具

就是一个强大的文本分析工具,相对于grep查找、sed的编辑,awk在对数据分析并生成报告的时候,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种处理。

  • awk是用来操作数据和产生报表的一种编程语言。数据可能来自标准输入、一个或多个文件或一个进程的输出等。awk可以用在命令行里进行简单操作,也可应用到较大的应用程序中。
  • awk是一门编程语法,作为unix工具来使用简化了很多,但是仍然有许多编程语言的特性,可以对目标进行一系列的处理。
  • 如果抛出awk的BEGIN和END,对文件的每行,awk都分两个阶段处理:

1、读取该行内容,分配临时寄存器,分配域名等操作;
2、对域做各种处理并输出;

  • awk基本用途

1、简单输出,如 awk ‘{print 1,NF}’ — print的规则
2、作为分隔符使用

单字符分隔符:打印系统中用户名和其他使用shell类型
单字符分隔符,管道连续使用awk:打印nginx日志中的访问目录。
多字符分隔符:抓取apache详细版本
多字符多个分隔符:截取ip地址
正则分隔符:截取ip地址

[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root    0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root   12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root  200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root    0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root   49 Jun 15 11:04 wrong.jpg

通过awk打印出第一列:

[root@admin test]# ls -l | awk '{print $1}'
total
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
drwxr-xr-x.
-rw-r--r--.

打印前两列,也就是前两个域:

[root@admin test]# ls -l | awk '{print $1,$2}'
total 16
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
drwxr-xr-x. 2
-rw-r--r--. 1

在awk中,抛开BEGIN和END不看,’{}’ 这是一个固定写法。

$ : 符号表示域,域之间通过默认的分隔符 空格 分开,如果有多个空格就会变成一个空格,第一个域为$1,第二个为$2等…

打印最后一列:

[root@admin test]# ls -l | awk '{print $NF}'
16
aa.jpg
ls.jpg
result.jpg
right.jpg
sd.ex
wrong.jpg

$NF : 就代表最后一列的意思。
如果想打印倒数第二列,则使用$(NF -1)。

[root@admin test]# ls -l | awk '{print $(NF - 1)}'
total
11:03
10:48
11:31
11:04
06:39
11:04

-F :参数-F是改变awk的默认分隔符,可以支持正则表达式。

[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root    0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root   12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root  200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root    0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root   49 Jun 15 11:04 wrong.jpg

awk默认的分隔符是 空格,空格前一列就是$1,后面是$2,$3…..
当我们改变默认分隔符为 分号 “:”时,打印一下结果:

[root@admin test]# ls -l | awk -F":" '{print $NF}'
total 16
03 aa.jpg
48 ls.jpg
31 result.jpg
04 right.jpg
39 sd.ex
04 wrong.jpg

通过-F 参数改变默认分隔符,以及其支持正则表达式的特性,精确的抽出ifconfig 文件中的ip地址。

#精确的抽取ip地址
[root@admin test]# ifconfig | grep "inet addr" | awk -F "addr:|  *" '{print $4}'
192.168.1.6
127.0.0.1

分析:

grep "inet addr" 表示先抽取出含有ip的那行,然后通过管道交给awk去处理

-F "addr:|  *" :表示该变默认分隔符为addr:或者连续多个空格

$4 :表示改变默认分隔符以后,ip地址在第4个域。

用grep实现抽取ip :

[root@admin home]# ifconfig | egrep -o "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"
192.168.1.6
192.168.1.255
255.255.255.0
127.0.0.1
255.0.0.0

然后在用head和tail抽取想要的ip即可。

使用sed实现抽取ip

[root@admin home]# ifconfig |grep "inet addr" | sed 's/inet addr://' | sed 's/Bcast.*//' | head -1
192.168.1.6
或者:
[root@admin home]# ifconfig |grep "inet addr" | sed 's/^.*addr://;s/  *.*//' | head -1
192.168.1.6

awk匹配打印

注:需要匹配的内容是写在{}外面的,通过//来体现匹配。
匹配打印实际上是文件每行读取的时候做的处理。

1、整行中匹配内容

awk '/sth/{print $1}' ---打印匹配sth的行的第一个域,这是对整行进行操作
awk '!/sth/{print $1}'  ---打印不匹配sth的行的第一个域

如匹配打印sd关键字:

[root@admin test]# ls
aa.jpg  ls.jpg  result.jpg  right.jpg  sd.ex  wrong.jpg

[root@admin test]# ls | awk '/sd/{print $1}'
sd.ex

2、域匹配内容

匹配某个文件中以默认分隔符分隔的$1域中含有关键字sth的行,然后打印出第一个域,命令如下:

awk '$1~/sth/{print $1}' 

3、改变awk中的默认分隔符

如打印出passwd文件中,以冒号 : 分隔的第一个域中含有关键字wcx行的第一个域:

[root@admin etc]# cat passwd | awk -F":" '$1~/wcx/{print $1}'
wcx

awk判断打印

判断打印实际上是文件每行读取后做的处理。
格式例如:

awk '{if($1=="wcx")print $1}'

if 判断必须是进入某一行以后才能做的,所以必须是在花括号{} 里面。而模式匹配是先匹配到内容的行才能进行下一步操作,所以模式匹配实在花括号{}外边。

[root@admin etc]# cat ./passwd | awk -F ":" '{if($1 == "wcx")print}
wcx:x:501:501::/home/wcx:/bin/bash

上面命令中,如果passwd文件中,以冒号分隔的第一个域==”wcx”,那么就打印出来,这是等于判断,所以只有wcx的行能出来。

BEGIN和END

在Unix awk中两个特别的表达式,BEGIN和END,这两者都可用于pattern中(参考前面的awk语法),提供BEGIN和END的作用是给程序赋予初始状态和在程序结束之后执行一些扫尾的工作。
任何在BEGIN之后列出的操作(在{}内)将在Unix awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。

awk的数组

有一个awk.txt文件,其内容如下:

[root@admin home]# cat awk.txt
kk kekea
jj d32
jame 23
sr
sr
kk
jame
wcx
jame 

统计一下awk.txt文件中每个人名出现的次数:

[root@admin home]# cat awk.txt | awk '{a[$1]++}END{for (i in a)print i,a[i]}'
wcx 1
jj 1
kk 2
sr 2
jame 3

解题思路分析:

  • {a[$1]++} :awk开始扫描管道传过来的内容,但是么有存储功能,现在我们需要计算每个人名出现的额次数,所以需要把扫描到的相同的人名分别存储起来,这时候就必须用数组,并且这里的数组都是hash数组,就是键值对的数组,这里的键—对应的就是对应域中的人名,如kk,值 — 对应的就是kk这个人名出现的次数。数组a中的下标$1代表的是第一个域中列名,如wcx。awk开始逐行扫描,每扫到一个相同的人名,对应的hash数组里的值就+1,如a[kk]++,直到扫到文件的结尾。(注:数组的默认值为空,如果要做++操作了,就会把里的值变成0)
  • END : END之后列出的操作将在Unix awk开始扫描完全部的输入之后执行。上面扫描完以后,就开始执行END后面的操作了。

  • {for (i in a)print i,a[i]} :利用for循环去遍历刚刚产生的hash数组,然后打印出数组下标所代表的值,也就是人名以及对数组中的值,也就是对应人名出现的次数。这里的i,就代表数组的下标,也就是域中的人名,如kk。

awk输出分隔符

awk默认的输出分隔符也是空格,如果想改变默认输出分隔符怎么做?

[root@admin home]# cat awk.txt | awk '{print $1,$2}'
kk kekea
jj d32
jame 23
sr 234
sr edr
kk 23
jame rt
wcx 88
jame 34

运行结果看出,打印时,$1和$2间加了逗号以后,输出的文本中域间就都是用空格隔开的。

如果我不加逗号,改为加一串别的字符时,输出后就以加入的字符作为分隔符了。如:

[root@admin home]# cat awk.txt | awk '{print $1"--"$2"BB"}'
kk--kekeaBB
jj--d32BB
jame--23BB
sr--234BB
sr--edrBB
kk--23BB
jame--rtBB
wcx--88BB
jame--34BB

通过awk批量修改扩展名

[root@admin test]# ls
aa.jpg  ls.jpg  result.jpg  right.jpg  wrong.jpg

将test目录下的以.jpg结尾的文件改成.txt结尾的文件:

[root@admin test]# ls | awk -F "." '{print "mv "$1"."$2" "$1".txt"}'|sh
[root@admin test]# ls
aa.txt  ls.txt  result.txt  right.txt  wrong.txt

或者:
[root@admin test]# ls | awk -F "." '{print "mv "$0" "$1".txt"}' | sh
#$0表示整行的内容

思路分析,同样改名也是需要构造mv a.txt a.jpg这样类似的语句的,这就用到了上面讲的输出时修改默认分隔符的操作了。构造出的mv语句再传给sh去执行就可以实现改名的操作。

标签:Linux 发布于:2019-11-14 20:33:32