文本三剑客之awk

grep:文本过滤器

grep ‘pattern’ input_file

sed:流编辑器

sed ‘command/pattern/修饰符’ filename

awk:报告生成器

主要功能是从文本文件中抽取符合条件的信息并以特定格式显示出来的特殊工具。

nawk:new awk

gawk:在一定程度上兼容awk和nawk,但又具备awk和nawk时所不具有的功能。

基本用法:

awk 【options】 ‘pattern {action}’ file1 file2

​ 选项 模式 处理

print和printf:

print:简单的打印格式

printf:可以自定义打印格式。

awk的工作原理:

1、awk使用一行作为输入,并将这一行赋值给变量$0,每一行也可称为一个记录,以换行符结束

2、然后,行被(默认为宫格或者制表符)分解成字段(或域),每个字段存储在已标号的变量中,从$1~$100。

3、awk如何知道用看空格来到分开字段呢?因为有一一个内部哦变量FS来确定字段分隔符。初始时,FS赋为空格。

4、awk打印字段时,将以设置的方法使用print函数打印,awk在打印的字段间加上空格,因为$1,$3之间有一个逗号。逗号比较特殊,他映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格。

5、awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串凤娥成为字段并进行处理,该过程将持续到所有行处理完毕

awk的基本处理机制:

一次从文件中读取一行文本,awk会相应的进行自动对齐并切片,将每一行按照分隔符,按照字符串的分隔符进行切割,默认切割符是空白(不管几个空格,都属于空白)

$0表示这一整行,每一列从$1开始算,

可以指定额外字符当作分隔符,

[root@localhost ~]# awk '{print $0}' test
this is a test.
[root@localhost ~]# awk '{print $4}' test
test.
[root@localhost ~]# awk '{print $1 $2}' test
thisis
[root@localhost ~]# awk '{print $1,$2}' test
this is

注意,域和域之间要用逗号分隔,但是输出的时候仍然是用空格分开的。

指定分隔符: -F(输出分隔符,就是读取文本的时候按照什么读取)

同时以两个作为分隔符:-F“【,|:】”

参数传递:-v
[root@localhost ~]# awk '{print $1,$2}' test
this is
[root@localhost ~]# awk 'BEGIN{OFS="#"}{print $1,$2}' test
this#is
——————>指定输出分隔符(要使用双引号)
[root@localhost ~]# awk 'BEGIN{OFS=":"}{print $1,$2,$3,$4}' test
this:is:a:test.
[root@localhost ~]# awk 'BEGIN{OFS=":"}{print $1,"hello",$2,$3,$4}' test
this:hello:is:a:test.
——————>插入字符串是要使用双引号
BEGIN:一种模式,后边如果没有文件则需要使用BEIN模式,用{}括起来
print的使用格式:
  1. 各项目之间要使用逗号隔开,而输出时则以空白字符隔开
  2. 输出的item可以为字符串或数值,当前记录的字段(如$1)、变量或者awk的表达式;数值会先转换成字符串,然后再输出
  3. print命令后边的item可以省略,此时其功能相当于print $0,因此,如果向输出空白行,则需要使用print “ ”;

eg:

[root@localhost ~]# awk 'BEGIN{print"line one\nline two\nline three"}'
line one
line two
line three
[root@localhost ~]# awk -F: '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3

[root@localhost ~]# date
Wed Sep 11 17:15:31 CST 2019
[root@localhost ~]# date |awk '{print "Month:",$2"\nYear:",$NF}'
Month: Sep
Year: 2019
printf的使用格式:

printf默认不会再行尾自动换行

printf format,item1,item2。。。。。

  • 1、其与print命令的最大不同是printf需要指定format;

  • 2、format用于指定后边的每个item的输出格式

    • format格式的指示符都是以%开头的,后边跟一个字符:

    • %c:显示字符的ASCⅡ码——>默认不会换行

    • [root@localhost ~]# awk 'BEGIN{printf "%c","a" }'
      a[root@localhost ~]# 
      
    • %d,%i:十进制数

    • [root@localhost ~]# awk  -F: '{printf "%5d\n",$3}' /etc/passwd
      以十进制数显示每个用户的用户ID
      [root@localhost ~]# awk  -F: '{printf "%-5d\n",$3}' /etc/passwd——>左对齐显示用户iD
      
      [root@localhost ~]# awk -F: '{printf "%-15s%-10s%-15s\n",$1,$2,$3}' /etc/passwd
      root           x         0
      bin            x         1
      
      [root@localhost ~]# awk -F: '{printf "|%-15s|%-10s|%-15s|\n",$1,$2,$3}' /etc/passwd
      |root           |x         |0              |
      
    • %e,%E:科学计数法显示数值

    • %f:显示浮点数

    • %g,%G:以科学计数法的格式或者浮点数的格式显示数值

    • %s:显示字符串

    • [root@localhost ~]# awk '{printf "%s\n",$1}' test
      this
      [root@localhost ~]# awk '{printf "%10s\n",$1}' test
            this——>默认右对齐,用10个字符串来显示
            [root@localhost ~]# awk '{printf "%-10s\n",$1}' test
      this      ——>左对齐,使用十个字符来显示
      [root@localhost ~]# awk '{printf "%-10s,%-10s\n",$1,$3}' test
      this      ,a         ——>显示两个字符
      
    • %u:无符号整数

    • %%:显示%本身

    • 修饰符:

    • N:显示宽度

    • -:左对齐——>% - c(不使用则表示右对齐)

    • +:显示数值符号

  • 3、printf语句不会自动打印换行符:\n

awk的变量:

可以内置变量,用户也可以自定义变量

awk内置变量之记录变量
  • FS:默认是空白字符——>读取文本时所使用的字段分隔符(指定输入字段分隔符)——>FS=“:“

  • RS:默认是换行——>输入文本信息所使用的换行符(指定输入行分隔符)

  • OFS:输出字段分隔符,显示内容的时候以什么为分隔符——>OFS=“#”

  • ORS:输出行分隔符。

awk内置变量之数据变量
  • NR:awk命令到目前为止已经处理了的行数(号码)。(所有文件的行数,包括正在处理的行)

  • FNR:相对当前正在处理的文件的行数

  • [root@localhost ~]# awk '{print FNR }' /etc/passwd test——>行数一块显示
    [root@localhost ~]# awk '{print NR }' /etc/passwd test——>行数各自显示
    
  • NF:当前正在处理的这一行有多少个字段。

  • $NF:表示在每一行的最后一个字段

[root@localhost ~]# awk '{print NF }' test
4
[root@localhost ~]# awk '{print $NF }' test
test.————>NF=4,所以$NF等于$4

gawk允许使用自定义自己的变量,只能使用字母,数字、下划线,且不能以数字开头,gawk区分大小写

在awk中给变量赋值使用赋值语句进行

例如:

[root@localhost ~]# awk -v test="hello" '{print test}'
^C
[root@localhost ~]# awk -v test="hello" 'BEGIN{print test}'
hello

第二种方式

[root@localhost ~]# awk 'BEGIN{test="hello" ;print test}'
hello
awk的操作符:

-x:负值

+x:转换为数值

x^y:

x**y:次方

x*y:乘法

x/y:除法

x+y:加法

x-y:减法

x%y:取余

赋值操作符:

=

+=

-=

*=

/=

%=

^=

**=

++

布尔值:

x<y

x<=y

x>y

x>=y

x==y:比较两个值是否相等

x!=y

x~y:x是一个字符串,y是一个模式,如果x能被y这个模式匹配到就为真。

x!~y:

awk模式和动作

任何awk语句都由模式和动作组成,模式部分决定动作语句何时触发以及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。模式可以是任何条件语句或者复合语句或者正则表达式。模式包括两个特殊字段BEGIN和END。使用BEGIN语句设置计数和打印头。BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作一句输入文本开始执行。END语句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态。

awk的模式类型:
1、正则表达式,格式为/pattarn/
1、匹配记录
[root@localhost ~]# awk -F: '/^r/{print $1}' /etc/passwd
root——>显示以r开头的行的第一个字段
2、匹配字段
[root@localhost ~]# awk -F: '$1~/^adm/' /etc/passwd
adm:x:3:4:adm:/var/adm:/sbin/nologin
————>第一个字段能被模式以adm开头匹配到的
[root@localhost ~]# awk -F: '$NF!~/bash$/' /etc/passwd
————>最后一个字段并不能被模式以bash结尾匹配到的
2、表达式:
[root@localhost ~]# awk -F: '$3>500{print $1,$3}' /etc/passwd
polkitd 999
mariadb 1000
fedora 1001
gentoo 1002
David 1003
Peter 1004
Jack 1005
Mike 1006
lxw 1007
ymy 1008
lxy 1009——显示用户ID大于500的用户名和用户ID
[root@localhost ~]# awk -F: '$7~"bash$"{print $1,$7}' /etc/passwd——>使用的就是x~y
root /bin/bash
fedora /bin/bash
gentoo /bin/bash
David /bin/bash
Peter /bin/bash
Jack /bin/bash
Mike /bin/bash
lxw /bin/bash
ymy /bin/bash
lxy /bin/bash——>显示用户的默认bash为/bin/bash的用户

awk -F: '$3==0' /etc/passwd
awk -F: '$3<10' /etc/passwd
awk -F: '$7=="/bin/bash"' /etc/passw
awk -F: '$1~/ymy/' /etc/passwd
df -P |grep '/' |awk '$4>25000'

3、指定匹配范围
[root@localhost ~]# awk -F: '/^r/,/^m/ {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
mail /sbin/nologin——>匹配第一个以r开头的行和第一个以m开头的行的中间所有行
4、特殊模式:BEGIN/END,仅在awk命令执行前运行一次/结束前运行一次

awk是一行一行读取文本的,BEGIN是仅在处理第一行之前先读取一次,END是仅在处理最后一行结束之后读取一次。都要使用{}括起来

[root@localhost ~]# awk -F: '/^r/,/^m/ {printf "%-10s%-10s%-10s\n",$1,$3,$7}' /etc/passwd
root      0         /bin/bash
bin       1         /sbin/nologin
daemon    2         /sbin/nologin
adm       3         /sbin/nologin
lp        4         /sbin/nologin
sync      5         /bin/sync
shutdown  6         /sbin/shutdown
halt      7         /sbin/halt
mail      8         /sbin/nologin
[root@localhost ~]# awk -F: 'BEGIN{print "USERNAME   ID      SHELL"} /^r/,/^m/{printf "%-10s%-10s%-10s\n",$1,$3,$7}END{print "end of file"}' /etc/passwd
USERNAME   ID      SHELL
root      0         /bin/bash
bin       1         /sbin/nologin
daemon    2         /sbin/nologin
adm       3         /sbin/nologin
lp        4         /sbin/nologin
sync      5         /bin/sync
shutdown  6         /sbin/shutdown
halt      7         /sbin/halt
mail      8         /sbin/nologin
end of file
5、空模式
[root@localhost ~]# awk -F: '{printf "%-10s%-10s%-10s\n",$1,$3,$7}' /etc/passwd

任何模式都没有指定,就是对文件的每一行都要处理。

常用的action:
1、表达式
2、控制语句(if、while、for、do while,case)
[root@localhost ~]# awk -F: '{if ($1=="root") print $1,"Admin";else print $1,"Common User"}' /etc/passwd
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
operator Common User
games Common User
ftp Common User

[root@localhost ~]# awk -F: '{if ($1=="root") printf "%-15s:%s\n",$1,"Admin";else printf "%-15s: %s\n",$1,"Common User"}' /etc/passwd——>更加美观
[root@localhost ~]# awk -F: '{if ($3==0) printf "%-15s%s\n",$1," is admin.";else printf "%-15s%s\n",$1," is pt."}' /etc/passwd

[root@localhost ~]# awk -F: '{i=1;while (i<=NF) {if (length($i)>=4) {print$i};i++}}' /etc/passwd
————>显示每一行中字符串的字符数大于4的字符串

[root@localhost ~]# awk -F: '{for(i=1;i<=NF;i++) {if (length($i)>=4) {print $i}}}' /etc/passwd————>同上

[root@localhost ~]# awk -F: '{shell[$NF]++}END{for(A in shell) {print A,shell[A]}}' /etc/passwd
/bin/sync 1
/bin/bash 10
/sbin/nologin 16
/sbin/halt 1
/sbin/shutdown 1
————>判断每种shell的用户个数

[root@localhost ~]# netstat -tan|awk '/^tcp/{STATE[$NF]++}END{for(S in STATE) {print S,STATE[S]}}'
LISTEN 4
ESTABLISHED 2————>判断正在监听tcp连接的状态个数

[root@localhost ~]# awk '{counts[$1]++};END{for(url in counts) print counts[url],url}' /var/log/httpd/access_log
——————>打印访问本机httpd服务的ip的访问次数
[root@localhost ~]# awk '{count[$1]++}END{for (ip in count) {printf "%-20s:%d\n",ip,count[ip]}}' /var/log/httpd/access_log
 192.168.15.110 :7
——————————————————同上

在这里插入图片描述

[root@localhost ~]# awk -F":" 'BEGIN{printf"%-15s%s\n","ShellType","Count"}{shellType[$NF]++}END{for(i in shellType)printf"%-15s%d\n",i,shellType[i]}' /etc/passwd
ShellType      Count
/bin/sync      1
/bin/bash      11
/sbin/nologin  18
/sbin/halt     1
/sbin/shutdown 1

在这里插入图片描述

3、复合语句
4、输入输出语句

awk练习

[root@localhost ~]# awk -F: '/root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin————>匹配含有root字符串的行
[root@localhost ~]# awk -F: '{print $1}' /etc/passwd————>以:为读取分隔符匹配第一行。
[[root@localhost ~]# awk -F: '/root/ {print $1,$3}' /etc/passwd
root 0
operator 11————>匹配含有root的行并且打印该行的第一个和第三个字符串
[root@localhost ~]# awk 'BEGIN{FS=":"} /root/ {print $1,$3}' /etc/passwd
root 0
operator 11
————>FS为读取文本时所指定的字符串
同时以两个作为分隔符FS=“[,|;]”

[root@localhost ~]# awk 'BEGIN{OFS=":"} /root/ {print $1,$3}' /etc/passwd
root:x:0:0:root:/root:/bin/bash:
operator:x:11:0:operator:/root:/sbin/nologin:
[root@localhost ~]# awk 'BEGIN{OFS=";"} /root/ {print $1,$3}' /etc/passwd
root:x:0:0:root:/root:/bin/bash;
operator:x:11:0:operator:/root:/sbin/nologin;
[root@localhost ~]# awk 'BEGIN{OFS=" "} /root/ {print $1,$3}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
————>OFS为指定输出分割字符,且awk是按行读取,所以只在每一行结尾有该符号。
[root@localhost ~]# df -P |grep '^/' |awk '$4>"60000" {print $4}'
8854780
94520
————>df显示磁盘使用情况-P使用POSIX格式显示,然后打印出磁盘使用情况大于60000的第四个字段
求一个域的和:awk 'BEGIN{sum=0}{sum=sum+$4}END{print sum}' filename
查看httpd访问前十名的IP以及次数
awk '{IPS[$1]++}END{for(i in IPS){print i,IPS[i]}}' /var/log/httpd/access_log |sort -nr -k2 |head -1000
注意不能使用$i,要使用i

先说简单的情况,假设shell中有一个变量a=“hello”
可以这么来处理
$ awk ‘BEGIN{print "’$a’"}’
这样做以后,经过shell处理,就变成了
$ awk ‘BEGIN{print "’___KaTeX parse error: Expected group after '_' at position 2: a_̲__'"}' __…a在shell中会被替换成hello
所以最终处理后的结果就变成了
$ awk ‘BEGIN{print “hello”}’

现在再说情况复杂一点的情况,a=“hello world”
如果还像上面那样做,$ awk ‘BEGIN{print "’___$a___’"}‘就会替换成 $ awk ‘BEGIN{print "hello’ ‘world"}’(也就是说,shell在把 a 替 换 成 h e l l o w o l r d 的 时 候 ( 注 意 这 里 的 a替换成hello wolrd的时候(注意这里的 ahellowolrd(a没有引号),
由于此时空格已非shell meta了,为了表示它只是一个普通的空格符
shell就给它加上了’ ')上面的解释错了,修改如下:

shell在把$ a替换成hello wolrd的时候,由于$a没有引号,
空格就被shell当成IFS解释了(也就是说,跟awk后面那个
空格在表示的意思上是完成一样的,都是shell meta)
这样这条awk命令其实就是由
awk
BEGIN{print “hello
world”}
这三部分组成
难怪awk报错了

这样,awk在解析的时候就变成了
$ awk ‘BEGIN{print "hello’___ 空格___‘world"}’
这么三段,那个原本应该是要被转义的空格
因为前面的’匹配错误的关系导致它变成了shell meta,使得awk没办法处理所以也就不是匹配出错的问题啦^ ^

按照上面的解释,就可以这么来修改,比如
$ awk ‘BEGIN{print "’"$ a"’"}’
或者
$ awk “BEGIN{print “$ a”}”
或者
$ awk BEGIN{print""$ a""}

二、进一步解释

第3种方法为什么要加两个双引号和一个单引号?

$ str=Hello

$ awk ‘BEGIN{print " ‘$str’ "}’

Hello

看上去是双引号套单引号,其实真正的原因为:

这是shell的功能,shell对单引号和双引号,按从左到右的顺序成对匹配

awk命令用单引号引起来,就是防止shell对其中内容进行解释

awk ‘{print " ‘$str’ "}’ file

实际上就是2部分

1:awk '{print " ’

2:’ " } ’

即awk对2个单引号内的命令起作用。

至于$str就被shell正常解释为变量str的值。

所以,如果str=hello,则经解释后成为,awk {print “hello”}file

而如果str=hello world,则解释时,在解释前一部分:awk {print " 后,在替换了变量后,变成了hello world,当shell读到hello和world中间的空格时,认为这是IFS,于是,把他们放在于不同的域中,这样解释成了:

awk BEGIN{print "hello

world"}两部分。

按照上面的解释,就可以这么来修改,比如

a)$ awk ‘BEGIN{print " ’ “$a” ’ "}’

或者

b)$ awk “BEGIN{print “$a”}”

或者

c)$ awk BEGIN{print""$a""}

也就是说:

变量中没有空格要用" ‘$a’ "

b变量中有空格要用:"" ’ a ′ " " 或 者   " a ' " "或者\ " a"" "a\ “或者\ {print\ “”$a”\ "\ }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值