Linux进阶_文本处理工具AWK

成功不易,加倍努力!

      • 文本处理三剑客之 awk
        • 1 awk 工作原理和基本用法说明
        • 2 动作 print
        • 3 awk变量
        • 4 动作 printf
        • 5 操作符
        • 6 模式PATTERN
        • 7 条件判断 if-else
        • 8 switch语句
        • 9 循环 do-while
        • 10 循环for
        • 11 continue和break
        • 12 next
        • 13 数组
        • 14 awk函数
          • 14.1 常见内置函数
          • 14.2 自定义函数
        • 15 awk脚本

文本处理三剑客之 awk

1 awk 工作原理和基本用法说明

awk:Aho, Weinberger, Kernighan,报告生成器,格式化文本输出,GNU/Linux发布的AWK目前由自由软件基金会(FSF)进行开发和维护,通常也称它为 GNU AWK

有多种版本:

  • AWK:原先来源于 AT & T 实验室的的AWK
  • NAWK:New awk,AT & T 实验室的AWK的升级版
  • GAWK:即GNU AWK。所有的GNU/Linux发布版都自带GAWK,它与AWK和NAWK完全兼容

gawk:模式扫描和处理语言,可以实现下面功能

  • 文本处理
  • 输出格式化的文本报表
  • 执行算数运算
  • 执行字符串操作

格式:

awk [options]  'program' var=value  file…
awk [options]  -f programfile  var=value file…

说明:
program通常是被放在单引号中,并可以由三种部分组成

  • BEGIN语句块
  • 模式匹配的通用语句块
  • END语句块

常见选项:

  • -F “分隔符” 指明输入时用到的字段分隔符,默认的分隔符是若干个连续空白符
  • -v var=value 变量赋值

Program格式

pattern{action statements;..}

pattern:决定动作语句何时触发及触发事件,比如:BEGIN,END,正则表达式等
action statements:对数据进行处理,放在{}内指明,常见:print, printf

awk 工作过程
Linux进阶_文本处理工具AWK_第1张图片
第一步:执行BEGIN{action;… }语句块中的语句

第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。

第三步:当读至输入流末尾时,执行END{action;…}语句块

  • BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
  • END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
  • pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块

分割符、域和记录

  • 由分隔符分隔的字段(列column,域field)标记$1,$2...$n称为域标识,$0为所有域,注意:和shell中变量$符含义不同
  • 文件的每一行称为记录record
  • 如果省略action,则默认执行 print $0 的操作

常用的action分类

  • output statements:print,printf
  • Expressions:算术,比较表达式等
  • Compound statements:组合语句
  • Control statements:if, while等
  • input statements

awk控制语句

  • { statements;… } 组合语句
  • if(condition) {statements;…}
  • if(condition) {statements;…} else {statements;…}
  • while(conditon) {statments;…}
  • do {statements;…} while(condition)
  • for(expr1;expr2;expr3) {statements;…}
  • break
  • continue
  • exit

2 动作 print

格式:

print item1, item2, ...

说明:

  • 逗号分隔符
  • 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
  • 如省略item,相当于print $0
  • 固定字符符需要用 " " 引起来,而变量和数字不需要

范例:

[root@centos8-B ~]#awk '{print "hello awk"}'
hi
hello awk
wow
hello awk
[root@centos8-B ~]#seq 10 |awk '{print "hello,awk"}'
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
[root@centos8-B ~]#seq 3 |awk '{print 2*3}'
6
6
6
[root@centos8-B ~]#awk -F: '{print "cui"}' /etc/passwd
[root@centos8 ~]#awk -F: '{print}' /etc/passwd
[root@centos8 ~]#awk -F: '{print $0}' /etc/passwd
[root@centos8 ~]#awk -F: '{print $1,$3}' /etc/passwd
[root@centos8 ~]#awk -F: '{print $1"\t"$3}' /etc/passwd

[root@centos8-B ~]#grep "^UUID" /etc/fstab |awk {'print $2,$3'}
/ xfs
/boot ext4
/date xfs
swap swap

面试题:取出网站访问量最大的前3个IP

[root@centos8-B ~]#awk '{print $1}' access_log |sort |uniq -c |sort -nr |head -3
   4870 172.20.116.228
   3429 172.20.116.208
   2834 172.20.0.222
[root@centos8-B ~]#awk '{print $1}' access_log |sort |uniq -c |sort -nr |head
   4870 172.20.116.228
   3429 172.20.116.208
   2834 172.20.0.222
   2613 172.20.112.14
   2267 172.20.0.227
   2262 172.20.116.179
   2259 172.20.65.65
   1565 172.20.0.76
   1482 172.20.0.200
   1110 172.20.28.145

面试题:取出分区利用率

[root@centos8-B ~]#df  |awk '{print $1,$5}'
Filesystem Use%
devtmpfs 0%
tmpfs 0%
tmpfs 1%
tmpfs 0%
/dev/sda2 11%
/dev/sda5 1%
/dev/sda1 19%
tmpfs 1%

#使用扩展的正则表达式
[root@centos8-B ~]#df  |awk -F"[[:space:]]++|%" '{print $5}'
Use
0
0
1
0
11
1
19
1
[root@centos8-B ~]#df  |awk -F"[[:space:]]++|%" '{print $1,$5}'
Filesystem Use
devtmpfs 0
tmpfs 0
tmpfs 1
tmpfs 0
/dev/sda2 11
/dev/sda5 1
/dev/sda1 19
tmpfs 1
[root@centos8-B ~]#df  |awk -F" ++|%" '{print $1,$5}'
Filesystem Use
devtmpfs 0
tmpfs 0
tmpfs 1
tmpfs 0
/dev/sda2 11
/dev/sda5 1
/dev/sda1 19
tmpfs 1

[root@centos8-B ~]#df |grep '^/dev/sd' |awk -F" ++|%" '{print $1,$5}'
/dev/sda2 11
/dev/sda5 1
/dev/sda1 19
[root@centos8-B ~]#df |grep '^/dev/sd' |awk -F" ++|%" '{print $5}'
11
1
19

[root@centos8-B ~]#df  |awk -F" ++|%" '/^\/dev\/sd/{print $1,$5}'
/dev/sda2 11
/dev/sda5 1
/dev/sda1 19
[root@centos8-B ~]#df  |awk -F" ++|%" '/^\/dev\/sd/{print $5}'
11
1
19

面试题:取 ifconfig 输出结果中的IP地址

[root@centos8-B ~]#ifconfig eth0 |awk '/netmask/{print $2}'
10.0.0.10
[root@centos6 ~]#ifconfig eth0 |awk -F" +|:" '/Mask/{print $4}'
10.0.0.6

[root@centos8-B ~]#ifconfig eth0 |sed -rn "2s/^[^0-9]+([0-9.]+) .*$/\1/p"
10.0.0.10
[root@centos6 ~]#ifconfig eth0 |sed -rn "2s/^[^0-9]+([0-9.]+) .*$/\1/p"
10.0.0.6

面试题:文件host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中

[root@centos8-B ~]#cat host_list.log
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
[root@centos8-B ~]#awk -F"[ .]" '{print $2}' host_list.log
www
blog
study
linux
python
[root@centos8-B ~]#awk -F"[ .]" '{print $2}' host_list.log >> host_list.log
[root@centos8-B ~]#cat host_list.log
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
www
blog
study
linux
python

3 awk变量

awk中的变量分为:内置和自定义变量

常见的内置变量

FS:输入自动分隔符,默认为空白字符;
OFS:输出自动分隔符,默认为空白字符;
RS:输入记录分隔符,指定输入时的换行符,原换行符仍有效;
ORS:输出记录分隔符,输出时用指定符号代替换行符;
NF:字段数量;
NR:行号;
FNR:各文件分别计数,记录号;
FILENAME:当前文件名;
ARGC:命令行参数的个数;
ARGV:数组,保存的是命令行所给定的各参数。

  • FS:输入字段分隔符,默认为空白字符,功能相当于 -F

范例:

awk -v FS=':' '{print $1,FS,$3}' /etc/passwd
awk -v FS=":" '{print $1FS$3}' /etc/passwd
awk -F: '{print $1,$3,$7}' /etc/passwd

S=:;awk -v FS=$S '{print $1FS$3}' /etc/passwd

[root@centos8-B ~]#awk -v FS=":" '{print $1FS$3}' /etc/passwd |head -n3
root:0
bin:1
daemon:2

[root@centos8-B ~]#S=:;awk -v FS=$S '{print $1FS$3}' /etc/passwd |head -n3
root:0
bin:1
daemon:2

#-F 和 FS变量功能一样,同时使用会冲突
[root@centos8-B ~]#awk -v FS=":" -F";" '{print $1FS$3}' /etc/passwd |head -n3
root:x:0:0:root:/root:/bin/bash;
bin:x:1:1:bin:/bin:/sbin/nologin;
daemon:x:2:2:daemon:/sbin:/sbin/nologin;
[root@centos8-B ~]#awk -F";" -v FS=":" '{print $1FS$3}' /etc/passwd |head -n3
root:0
bin:1
daemon:2
  • OFS:输出字段分隔符,默认为空白字符

范例:

[root@centos8-B ~]#awk -v FS=":" '{print $1,$3,$7}' /etc/passwd |head -n1
root 0 /bin/bash

[root@centos8-B ~]#awk -v FS=":" -v OFS=":" '{print $1,$3,$7}' /etc/passwd |head -n1
root:0:/bin/bash

  • RS:输入记录record分隔符,指定输入时的换行符

范例:

awk -v RS=' ' '{print }' /etc/passwd
  • ORS:输出记录分隔符,输出时用指定符号代替换行符

范例:

awk -v RS=' ' -v ORS='###' '{print $0}' /etc/passwd
  • NF:字段数量

范例:

#引用变量时,变量前不需加$
[root@centos8-B ~]#awk -F: '{print NF}' /etc/fstab
[root@centos8-B ~]#awk -F: '{print $(NF-1)}' /etc/passwd
[root@centos8-B ~]#ls /mnt/BaseOS/Packages/*.rpm |awk -F"." '{print $(NF-1)}' |sort |uniq -c
    389 i686
    208 noarch
   1060 x86_64

面试题:连接数最多的前3个IP

[root@centos8-B ~]#awk -F" +|:" '{print $(NF-2)}' ss.log |sort |uniq -c |sort -nr |head -n3
     44 127.0.0.1
     10 113.234.28.244
      8 124.64.18.135
[root@centos8-B ~]#awk -F" +|:" '/^ESTAB/{print $(NF-2)}' ss.log |sort |uniq -c |sort -nr |head -n3
     44 127.0.0.1
     10 113.234.28.244
      8 124.64.18.135

[root@centos8-B ~]#ss -nt |grep "^ESTAB" |awk -F"[[:space:]]+|:" '{print $(NF -2)}'
10.0.0.1
10.0.0.7
10.0.0.1
[root@centos8-B ~]#ss -nt |awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF -2)}'
10.0.0.1
10.0.0.7
10.0.0.1

[root@centos8-B ~]#ss -nt |awk -F: '{print $(NF -1)}' |awk '/^[0-9]/{print $NF}' |sort |uniq -c |head -n3
      2 10.0.0.1

范例:每十分钟检查将连接数超过100个以上的IP放入黑名单拒绝访问

[root@centos8 ~]#cat deny_dos.sh
LINK=100
while true;do
	ss -nt | awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}'|sort |uniq - c|while read count ip;do 
		if [ $count -gt $LINK ];then
			iptables -A INPUT -s $ip -j REJECT
		fi
	done
done
[root@centos8 ~]#chmod +x /root/deny_dos.sh
[root@centos8 ~]#crontab -e
[root@centos8 ~]#crontab -l 
*/10 * * * *   /root/deny_dos.sh

范例:

[root@centos8 ~]#cat deny_dos.sh
IPLIST=`awk -F" +|:" '/^ESTAB/{print $(NF-2)}' ss.log |sort |uniq -c|sort -
nr|head -3|awk '{print $2}'`
for ip in $IPLIST;do
	iptables -A INPUT -s $ip -j REJECT
done
  • NR:记录的编号

范例:

[root@centos8-B ~]#awk '{print NR,$0}' /etc/issue /etc/centos-release
1 \S
2 Kernel \r on an \m
3 
4 CentOS Linux release 8.0.1905 (Core)

范例:取ifconfig输出结果中的IP地址

[root@centos8-B ~]#ifconfig eth0 |awk '/netmask/{print $2}'
10.0.0.10
[root@centos8-B ~]#ifconfig eth0 |awk 'NR==2{print $2}'
10.0.0.10

范例:

[root@centos8 ~]#awk -F: '{print NR}' /etc/passwd
123
......
[root@centos8 ~]#awk -F: 'END{print NR}' /etc/passwd
57
[root@centos8 ~]#awk -F: 'BEGIN{print NR}' /etc/passwd
0
  • FNR:各文件分别计数,记录的编号

范例:

awk '{print FNR}' /etc/fstab /etc/inittab

[root@centos8-B ~]#awk '{print NR,$0}' /etc/issue /etc/redhat-release 
1 \S
2 Kernel \r on an \m
3 
4 CentOS Linux release 8.0.1905 (Core) 
[root@centos8-B ~]#awk '{print FNR,$0}' /etc/issue /etc/redhat-release 
1 \S
2 Kernel \r on an \m
3 
1 CentOS Linux release 8.0.1905 (Core)
  • FILENAME:当前文件名

范例:

[root@centos8-B ~]#awk '{print FILENAME}' /etc/issue 
/etc/issue
/etc/issue
/etc/issue

[root@centos8-B ~]#awk '{print FNR,FILENAME,$0}' /etc/issue /etc/redhat-release 
1 /etc/issue \S
2 /etc/issue Kernel \r on an \m
3 /etc/issue 
1 /etc/redhat-release CentOS Linux release 8.0.1905 (Core)  
  • ARGC:命令行参数的个数

范例:

[root@centos8-B ~]#awk '{print ARGC}' /etc/issue /etc/redhat-release
3
3
3
3
[root@centos8-B ~]#awk 'BEGIN{print ARGC}' /etc/issue /etc/redhat-release
3
  • ARGV:数组,保存的是命令行所给定的各参数,每一个参数:ARGV[0],…

范例:

[root@centos8 ~]#awk 'BEGIN{print ARGV[0]}' /etc/issue /etc/redhat-release 
awk
[root@centos8 ~]#awk 'BEGIN{print ARGV[1]}' /etc/issue /etc/redhat-release 
/etc/issue
[root@centos8 ~]#awk 'BEGIN{print ARGV[2]}' /etc/issue /etc/redhat-release 
/etc/redhat-release
[root@centos8 ~]#awk 'BEGIN{print ARGV[3]}' /etc/issue /etc/redhat-release

[root@centos8 ~]#

自定义变量(区分字符大小写)

  • -v var=value
  • 在program中直接定义

范例:

[root@centos8-B ~]#awk -F: -v name='username:' '{print name,$1}' awktest.txt
username: root
username: bin
username: daemon
username: adm
username: lp
username: sync
username: shutdown
username: halt
username: mail
username: operator
[root@centos8-B ~]#awk  -v test='hello gawk' '{print test}' /etc/fstabhello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
hello gawk
[root@centos8-B ~]#awk  -v test='hello gawk' 'BEGIN{print test}'
hello gawk
[root@centos8-B ~]#awk  'BEGIN{test="hello,gawk";print test}'
hello,gawk

[root@centos8-B ~]#awk  -F: '{sex="male";print $1,sex,age;age=18}' awktest.txt 
root male 
bin male 18
daemon male 18
adm male 18
lp male 18
sync male 18
shutdown male 18
halt male 18
mail male 18
operator male 18


[root@centos8-B ~]#echo '{name="mmagedu";age=20;print name,$1,age}' > awkscript
[root@centos8-B ~]#cat awkscript
{name="mmagedu";age=20;print name,$1,age}
[root@centos8-B ~]#awk -F: -f awkscript awktest.txt 
mmagedu root 20
mmagedu bin 20
mmagedu daemon 20
mmagedu adm 20
mmagedu lp 20
mmagedu sync 20
mmagedu shutdown 20
mmagedu halt 20
mmagedu mail 20
mmagedu operator 20

cat awkscript
{print script,$1,$2}
awk  -F: -f awkscript script="awk" /etc/passwd

4 动作 printf

printf 可以实现格式化输出

格式:

printf “FORMAT”, item1, item2, ...

说明:

  • 必须指定FORMAT
  • 不会自动换行,需要显式给出换行控制符 \n
  • FORMAT中需要分别为后面每个item指定格式符

格式符:与item一一对应

  • %c:显示字符的ASCII码
  • %d, %i:显示十进制整数
  • %e, %E:显示科学计数法数值
  • %f:显示为浮点数
  • %g, %G:以科学计数法或浮点形式显示数值
  • %s:显示字符串
  • %u:无符号整数
  • %%:显示%自身

修饰符

#[.#] 第一个数字控制显示的宽度;第二个#表示小数点后精度,如:%3.1f
- 左对齐(默认右对齐) 如:%-15s
+   显示数值的正负符号   如:%+d

范例:

awk -F:   '{printf "%s",$1}' /etc/passwd
awk -F:   '{printf "%s\n",$1}' /etc/passwd
awk -F:   '{printf "%20s\n",$1}' /etc/passwd
awk -F:   '{printf "%-20s\n",$1}' /etc/passwd
awk -F:   '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F:   '{printf "Username: %s\n",$1}' /etc/passwd
awk -F:   '{printf “Username: %sUID:%d\n",$1,$3}' /etc/passwd
awk -F:   '{printf "Username: %25sUID:%d\n",$1,$3}' /etc/passwd
awk -F:   '{printf "Username: %-25sUID:%d\n",$1,$3}' /etc/passwd

[root@centos8-B ~]#awk -F: '{printf "%20s %10d\n",$1,$3}' awktest.txt                 root          0
                 bin          1
              daemon          2
                 adm          3
                  lp          4
                sync          5
            shutdown          6
                halt          7
                mail          8
            operator         11
[root@centos8-B ~]#awk -F: '{printf "%-20s %10d\n",$1,$3}' awktest.txt 
root                          0
bin                           1
daemon                        2
adm                           3
lp                            4
sync                          5
shutdown                      6
halt                          7
mail                          8
operator                     11

#以冒号为分隔符,每行输出”username:第一个字段“,然后换行
[root@centos8-B ~]#awk -F: '{printf "username:%s\n",$1}' awktest.txt 
username:root
username:bin
username:daemon
username:adm
username:lp
username:sync
username:shutdown
username:halt
username:mail
username:operator

#以冒号为分隔符,每行输出”username:第一个字段,左对齐,占20个宽度,UID:第三个字段“,然后换行
[root@centos8-B ~]#awk -F: '{printf "username:%-20s uid:%d\n",$1,$3}' awktest.txt 
username:root                 uid:0
username:bin                  uid:1
username:daemon               uid:2
username:adm                  uid:3
username:lp                   uid:4
username:sync                 uid:5
username:shutdown             uid:6
username:halt                 uid:7
username:mail                 uid:8
username:operator             uid:11

5 操作符

算术操作符:

x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值

字符串操作符:没有符号的操作符,字符串连接

赋值操作符:

=:右边赋值给左边
+=:先加,再赋值
-=:先减,再赋值
*=:先乘,再赋值
/=:先除,再赋值
%=:先取余,再赋值
^=:先幂运算,再赋值
++:递增操作
--:递减操作

范例:

[root@centos8 ~]#awk 'BEGIN{i=0;print i++,i}'   #先赋值再加
0 1
[root@centos8 ~]#awk 'BEGIN{i=0;print ++i,i}'   #先加再赋值
1 1

范例:

[root@centos8 ~]#awk -v n=0 '!n++' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@centos8 ~]#awk -v n=0 '!n++{print n}' /etc/passwd
1
[root@centos8 ~]#awk -v n=1 '!n++{print n}' /etc/passwd
[root@centos8 ~]#awk -v n=0 '!++n{print n}' /etc/passwd
[root@centos8 ~]#awk -v n=0 '!++n' /etc/passwd
[root@centos8 ~]#awk -v n=-1 '!++n' /etc/passwd
root:x:0:0:root:/root:/bin/bash

比较操作符:

 ==:判断相等
 !=:判断不等
 \>:判断大于
 \>=:判断大于等于
 <:判断小于
 <=:判断小于等于

范例:

[root@centos8 ~]#awk 'NR==2' /etc/issue
Kernel \r on an \m
[root@centos8 ~]#awk -F: '$3>=1000' /etc/passwd
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
wang:x:1000:1000:wang:/home/wang:/bin/bash
mage:x:1001:1001::/home/mage:/bin/bash

范例:取奇,偶数行

[root@centos8 ~]#seq 10 | awk 'NR%2==0'
2
4
6
8
10
[root@centos8 ~]#seq 10 | awk 'NR%2==1'
1
3
5
7
9
[root@centos8 ~]#seq 10 | awk 'NR%2!=0'
1
3
5
7
9

模式匹配符:

~ 左边是否和右边匹配,包含关系
!~ 是否不匹配

范例:

[root@centos8-B ~]#awk -F: '$0 ~ /root/{print $0}' awktest.txt 
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@centos8-B ~]#awk -F: '$0 !~ /root/{print $0}' awktest.txt 
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

[root@centos8 ~]#awk -F: '$0 ~ "^root"{print $1}' /etc/passwd
root
[root@centos8 ~]#awk '$0 !~ /root/'   /etc/passwd

[root@centos8 ~]#awk '/root/'   /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

[root@centos8 ~]#awk -F: '$3==0'     /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@centos8 ~]#df | awk -F"[[:space:]]+|%" '$0 ~ /^\/dev\/sd/{print $5}'
11
1
19

[root@centos8 ~]#ifconfig eth0 | awk 'NR==2{print $2}'
10.0.0.8

逻辑操作符:

与:&&,并且关系
或:||,或者关系
非:!,取反

范例:!取反

[root@centos8 ~]#awk 'BEGIN{print i}'
[root@centos8 ~]#awk 'BEGIN{print !i}'
1
[root@centos8 ~]#awk -v i=10 'BEGIN{print !i}'
0
[root@centos8 ~]#awk -v i=-3 'BEGIN{print !i}'
0
[root@centos8 ~]#awk -v i=0 'BEGIN{print !i}'
1
[root@centos8 ~]#awk -v i=abc 'BEGIN{print !i}'
0
[root@centos8 ~]#awk -v i='' 'BEGIN{print !i}'
1

[root@centos8-B ~]#seq 10 |awk 'i=!i'
1
3
5
7
9
[root@centos8-B ~]#seq 10 |awk '!(i=!i)'
2
4
6
8
10

范例:

awk -F:   '$3>=0 && $3<=1000 {print $1,$3}' /etc/passwd
awk -F:   '$3==0 || $3>=1000 {print $1,$3}' /etc/passwd 
awk -F:   '!($3==0) {print $1,$3}'     /etc/passwd
awk -F:   '!($3>=500) {print $1,$3}' /etc/passwd

条件表达式(三目表达式)

selector?if-true-expression:if-false-expression

范例:

[root@centos8-B ~]#awk -F: '{$3>=500?typeuser="common user":typeuser="sysuser" ;printf "%-15s %-20s %10d\n",typeuser,$1,$3}' awktest.txt 
sysuser         root                          0
sysuser         bin                           1
sysuser         daemon                        2
sysuser         adm                           3
sysuser         lp                            4
sysuser         sync                          5
sysuser         shutdown                      6
sysuser         halt                          7
sysuser         mail                          8
sysuser         operator                     11

6 模式PATTERN

PATTERN:根据pattern条件,过滤匹配的行,再做处理

PATTERN:根据pattern条件,过滤匹配的行,再做处理:
1. 如果未指定:空模式,匹配每一行;
2. /regular expression/:仅处理能够模式匹配到的行,需要用//扩起来;
3. relational expression:关系表达式,结果为真,才会被处理;
    真:结果为非0值,非空字符串都是真;
    假:结果为空字符串或0值都是假。
4. line ranges:行范围;
  startine,endline:/pat1/,/pat2/不支持直接给出数子格式。
5. BEGIN/END模式
  BEGIN{}:仅在开始处理文件中的文本之前执行一次;
  END{}:仅在文本处理完成之后执行一次。
  1. 如果未指定:空模式,匹配每一行
    范例:
[root@centos8 ~]#awk -F: '{print $1,$3}' /etc/passwd
  1. /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
    范例:
[root@centos8 ~]#awk   '/^UUID/{print $1}'     /etc/fstab
[root@centos8 ~]#awk   '!/^UUID/{print $1}'   /etc/fstab
[root@centos8 ~]#df | awk '/^\/dev\/sd/'
/dev/sda2      104806400 4935924  99870476   5% /
/dev/sda3       52403200  398876  52004324   1% /data
/dev/sda1         999320  848572     81936  92% /boot
  1. relational expression: 关系表达式,结果为“真”才会被处理
    真:结果为非0值,非空字符串
    假:结果为空字符串或0值

范例:

[root@centos8 ~]#awk '!1' /etc/passwd
[root@centos8 ~]#awk '!0' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
.......

[root@centos8 ~]#awk '1' /etc/issue
\S
Kernel \r on an \m

[root@centos8 ~]#awk '0' /etc/issue
[root@centos8 ~]#awk '"magedu"' /etc/issue
\S
Kernel \r on an \m

[root@centos8 ~]#awk '""' /etc/issue
[root@centos8 ~]#awk 'magedu' /etc/issue
[root@centos8 ~]#awk 'magedu' /etc/issue
[root@centos8 ~]#awk -v magedu=wang 'magedu' /etc/issue
\S
Kernel \r on an \m

[root@centos8 ~]#awk -v magedu="" 'magedu' /etc/issue
[root@centos8 ~]#awk -v magedu="0" 'magedu' /etc/issue
[root@centos8 ~]#awk -v magedu=0 'magedu' /etc/issue

[root@centos8 ~]#awk '"0"' /etc/issue
\S
Kernel \r on an \m

[root@centos8 ~]#awk '0' /etc/issue

范例:

awk   -F:  'i=1;j=1{print i,j}' /etc/passwd

awk  -F: '$3>=1000{print $1,$3}' /etc/passwd
awk  -F: '$3<1000{print $1,$3}' /etc/passwd
awk  -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd

[root@centos8 ~]#awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
root /bin/bash
wang /bin/bash
mage /bin/bash

[root@centos8 ~]#awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
root /bin/bash
wang /bin/bash
mage /bin/bash
  1. line ranges:行范围

不支持直接用行号,但可以使用变量NR间接指定行号

/pat1/,/pat2/ 不支持直接给出数字格式

范例:

[root@centos8 ~]#seq 10 | awk 'NR>=3 && NR<=6'
3
4
5
6

[root@centos8 ~]#awk 'NR>=3 && NR<=6{print NR,$0}' /etc/passwd
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync

[root@centos8 ~]#sed -n '3,6p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

[root@centos8 ~]#awk '/^bin/,/^adm/' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
[root@centos8 ~]#sed -n '/^bin/,/^adm/p' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
  1. BEGIN/END模式

BEGIN{}:仅在开始处理文件中的文本之前执行一次

END{}:仅在文本处理完成之后执行一次

范例

awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "END FILE"}' 
/etc/passwd
awk -F: '{print "USER USERID";print $1":"$3} END{print "END FILE"}' /etc/passwd
awk -F: 'BEGIN{print "USER UID \n--------------- "}{print $1,$3}' /etc/passwd
awk -F: 'BEGIN{print "USER UID \n--------"}{print $1,$3}END{print 
"=========="}' /etc/passwd
[root@centos8-B ~]#awk -F: 'BEGIN{print "user                id\n----------------------"}{printf "%-10s| %10d\n", $1,$3}END{print "----------------------"}' awktest.txt 
user                id
----------------------
root      |          0
bin       |          1
daemon    |          2
adm       |          3
lp        |          4
sync      |          5
shutdown  |          6
halt      |          7
mail      |          8
operator  |         11
----------------------

范例:

seq 10 | awk   'i=0'
seq 10 | awk   'i=1'
seq 10 | awk   'i=!i'
seq 10 | awk   '{i=!i;print i}'
seq 10 | awk   '!(i=!i)'              
seq 10 | awk -v i=1 'i=!i'

[root@centos8-B ~]#seq 10 |awk 'i=0'
[root@centos8-B ~]#
[root@centos8-B ~]#seq 10 |awk 'i=1'
1
2
3
4
5
6
7
8
9
10
[root@centos8-B ~]#seq 10 |awk 'i=!i'
1
3
5
7
9
[root@centos8-B ~]#seq 10 |awk '!(i=!i)'
2
4
6
8
10
[root@centos8-B ~]#seq 10 |awk -v i=1 'i=!i'
2
4
6
8
10
[root@centos8-B ~]#seq 10 |awk -v i=0 'i=!i'
1
3
5
7
9
[root@centos8-B ~]#seq 10 |awk '{i=!i;print i}'
1
0
1
0
1
0
1
0
1
0

7 条件判断 if-else

语法:

if(condition){statement;}[else statement]
if(condition1){statement1}else if(condition2){statement2}else if(condition3)
{statement3}......else{statementN}

使用场景:对awk取得的整行或某个字段做条件判断

范例:

awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or 
Sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($3>=1000) printf "Common user: %s\n",$1; else printf "root or 
Sysuser: %s\n",$1}' /etc/passwd

df -h|awk -F% '/^\/dev\/sd/{print $1}'| awk '$NF>=80{print $1,$5}'
df | awk -F"[[:space:]]+|%" '/^\/dev\/sd/{if($5>80)print $1,$5}'

[root@centos8 ~]#df | awk -F' +|%' '/^\/dev\/sd/{if($5>=10)print $1,$5}'
/dev/sda1 15

awk 'BEGIN{ test=100;if(test>90){print "very good"}
 else if(test>60){ print "good"}else{print "no pass"}}'

8 switch语句

语法:

switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or 
/REGEXP2/: statement2; ...; default: statementn}

条件“真”,进入循环;条件“假”,退出循环
使用场景:

  • 对一行内的多个字段逐一类似处理时使用
  • 对数组中的各元素逐一处理时使用

示例:

#内置函数length()返回字符数,而非字节数

[root@centos8-B ~]#awk 'BEGIN{print length("hello")}'
5
[root@centos8-B ~]#awk 'BEGIN{print length("马哥教育")}'
4

[root@Centos7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1127.el7.x86_64 31
root=UUID=eeb6c8d3-c590-4cb0-9d48-8c2cc415c933 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-440a03bbbe024e0287063cec5bb90e06 50
root=UUID=eeb6c8d3-c590-4cb0-9d48-8c2cc415c933 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13

[root@Centos7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){if(length($i)>=10){print $i,length($i)};i++}}' /etc/grub2.cfg
/vmlinuz-3.10.0-1127.el7.x86_64 31
root=UUID=eeb6c8d3-c590-4cb0-9d48-8c2cc415c933 46
crashkernel=auto 16
net.ifnames=0 13
/vmlinuz-0-rescue-440a03bbbe024e0287063cec5bb90e06 50
root=UUID=eeb6c8d3-c590-4cb0-9d48-8c2cc415c933 46
crashkernel=auto 16
net.ifnames=0 13

#计算1+2+3+...+100
[root@Centos7 ~]#awk 'BEGIN{ total=0;i=1;while(i<=100){total+=i;i++};print total}'
5050

9 循环 do-while

语法:

do {statement;}while(condition)

意义:无论真假,至少执行一次循环体

范例:

[root@Centos7 ~]#awk 'BEGIN{ total=0;i=1;do{ total+=i;i++;}while(i<=100);print total}'
5050

10 循环for

语法:

for(expr1;expr2;expr3) {statement;}

常见用法:

for(variable assignment;condition;iteration process) {for-body}

特殊用法:能够遍历数组中的元素

for(var in array) {for-body}

范例:

[root@centos8 ~]#awk 'BEGIN{total=0;for(i=1;i<=100;i++){total+=i};print total}'
5050

范例:

[root@Centos7 ~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++){print $i,length($i)}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1127.el7.x86_64 31
root=UUID=eeb6c8d3-c590-4cb0-9d48-8c2cc415c933 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-440a03bbbe024e0287063cec5bb90e06 50
root=UUID=eeb6c8d3-c590-4cb0-9d48-8c2cc415c933 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13

性能比较

time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}')
time (total=0;for i in {1..10000};do total=$(($total+i));done;echo $total)
time (for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
time (seq –s ”+” 10000|bc)

11 continue和break

continue 中断本次循环

break 中断整个循环

格式:

continue [n]
break [n]

范例:

[root@centos8-B ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
2500
[root@centos8-B ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)break;sum+=i}print sum}'
1225

12 next

next 可以提前结束对本行处理而直接进入下一行处理(awk自身循环)

范例:

[root@centos8-B ~]#awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
games 12
ftp 14
nobody 65534
polkitd 998
rtkit 172
unbound 996
rpc 32
chrony 994
saslauth 992
dnsmasq 986
cockpit-ws 984
sssd 982
pipewire 980
gdm 42
insights 978
sshd 74
avahi 70
tcpdump 72
cui 1000

13 数组

awk的数组为关联数组

格式

array_name[index-expression]

范例:

weekdays["mon"]="Monday"

index-expression

  • 利用数组,实现 k/v 功能
  • 可使用任意字符串;字符串要使用双引号括起来
  • 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
  • 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历

范例:

[root@centos8-B ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]=Tuesday;print weekdays["mon"]}'
Monday

范例:

awk '!line[$0]++' dupfile
awk '{!line[$0]++;print $0, line[$0]}' dupfile

范例:判断数组索引是否存在

[root@centos8-B ~]#awk 'BEGIN{array["i"]="x";array["j"]="y" ;if("i" in array ){print "存在"}else{print "不存在"}}'
存在
[root@centos8-B ~]#awk 'BEGIN{array["i"]="x";array["j"]="y" ;if("abc" in array ){print "存在"}else{print "不存在"}}'
不存在

若要遍历数组中的每个元素,要使用for循环

for(var in array) {for-body}

注意:var会遍历array的每个索引

范例:遍历数组

[root@centos8-B ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays){print weekdays[i]}}'
Tuesday
Monday

[root@centos8-B ~]#awk 'BEGIN{students[1]="daizong";students[2]="junzong";students[3]="kunzong";for(x in students){print x":"students[x]}}'
1:daizong
2:junzong
3:kunzong

[root@centos8-B ~]#awk 'BEGIN {
> a["x"] = "welcome"
> a["y"] = "to"
> a["z"] = "Magedu"
> for (i in a) {
> print i,a[i]
> }
> }'
x welcome
y to
z Magedu

[root@centos8-B ~]#awk -F: '{user[$1]=$3}END{for(i in user){print "username:"i,"uid: "user[i]}}' /etc/passwd
username:adm uid: 3
username:rpc uid: 32
username:dnsmasq uid: 986
username:radvd uid: 75
username:sync uid: 5
username:mail uid: 8
username:tss uid: 59
username:gluster uid: 995
username:unbound uid: 996
username:halt uid: 7

面试题:显示主机的连接状态出现的次数

[root@centos8-B ~]#awk 'NR!=1{print $1}' ss.log |sort |uniq -c
    108 ESTAB
      1 FIN-WAIT-1
      3 LAST-ACK

[root@centos8-B ~]#cat ss.log | sed -nr '1!s/^([^0-9]+) .*/\1/p'|sort |uniq -c
    108 ESTAB     
      3 LAST-ACK  

[root@centos8-B ~]#ss -ant | awk 'NR!=1{state[$1]++}END{for(i in state){print i,state[i]}}'
LISTEN 10
ESTAB 1

[root@centos8-B ~]#netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state){print i,state[i]}}'
LISTEN 10
ESTABLISHED 1

范例:

[root@centos8-B ~]#awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log 
172.20.0.200 1482
172.20.21.121 2
172.20.30.91 29
172.16.102.29 864
172.20.0.76 1565
172.20.9.9 15
172.20.1.125 463
172.20.61.11 2

[root@centos8-B ~]#awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log |sort -k2 -nr |head -3
172.20.116.228 4870
172.20.116.208 3429
172.20.0.222 2834
[root@centos8-B ~]#awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' access_log |sort -nr |head -3
4870 172.20.116.228
3429 172.20.116.208
2834 172.20.0.222

面试题:封掉查看访问日志中连接次数超过1000次的IP

awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){system("iptables -A INPUT -s "i" -j REJECT")}}}' access_log 

范例:多维数组(了解)

[root@centos8 ~]#awk 'BEGIN{
> array[1][1]=11
> array[1][2]=12
> array[1][3]=13
> array[2][1]=21
> array[2][2]=22
> array[2][3]=23
> for (i in array)
>     for (j in array[i])
>         print array[i][j]
> }'
11
12
13
21
22
23

14 awk函数

awk 的函数分为内置和自定义函数

14.1 常见内置函数

数值处理:

  • rand():返回0和1之间一个随机数
  • srand():配合rand() 函数,生成随机数的种子
  • int():返回整数

范例:

[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.790437
[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.283736
[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.948082
[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.371798

[root@centos8-B ~]#awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100)}'
47
97
61
44
88
88
18
41
33
27

字符串处理:

  • length([s]):返回指定字符串的长度
  • sub(r,s,[t]):对t字符串搜索r表示模式匹配的内容,并将第一个匹配内容替换为s

范例:

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",$1);print $0}'
2008-08:08 08:08:08

gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容

范例:

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
2008-08-08 08-08-08
[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$0);print $0}'
2008-08-08 08-08-08

split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

范例:

[root@centos8-B ~]#netstat -tn | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}
10.0.0.1 1
10.0.0.6 1
10.0.0.7 673

system 函数:可以awk中调用shell命令
空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用""引用起来

14.2 自定义函数

自定义函数格式:

function name ( parameter, parameter, ... ) {
   statements
   return expression
}

范例:

[root@centos8 ~]#cat func.awk
function max(x,y) {
	x>y?var=x:var=y
	return var
}
BEGIN{print max(a,b)}
[root@centos8 ~]#awk -v a=30 -v b=20 -f func.awk
30

15 awk脚本

将awk程序写成脚本,直接调用或执行

范例

[root@centos8 ~]#cat passwd.awk 
{if($3>=1000)print $1,$3}
[root@centos8 ~]#awk -F: -f passwd.awk /etc/passwd
nobody 65534
wang 1000
mage 1001

范例:

[root@centos8 ~]#cat test.awk
#!/bin/awk -f
#this is a awk script
{if($3>=1000)print $1,$3}
[root@centos8 ~]#chmod +x test.awk
[root@centos8 ~]#./test.awk -F: /etc/passwd
nobody 65534
wang 1000
mage 1001

向awk脚本传递参数

格式:

awkfile var=value var2=value2... Inputfile

==注意:==在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数

范例:

[root@centos8 ~]#cat test2.awk 
#!/bin/awk -f
{if($3 >=min && $3<=max)print $1,$3} 
[root@centos8 ~]#chmod +x test2.awk
[root@centos8 ~]#./test2.awk -F: min=100 max=200 /etc/passwd
systemd-resolve 193
rtkit 172
pulse 171
qemu 107
usbmuxd 113
abrt 173

练习
1、文件host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中

1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
......
999 study.magedu.com

2、统计/etc/fstab文件中每个文件系统类型出现的次数
3、统计/etc/fstab文件中每个单词出现的次数
4、提取出字符串Yd$C@M05MB%9&Bdh7dq+YVixp3vpw中的所有数字
5、有一文件记录了1-100000之间随机的整数共5000个,存储的格式100,50,35,89…请取出其中最大和最小的整数
6、解决Dos攻击生产案例:根据web日志或者或者网络连接数,监控当某个IP并发连接数或者短时内PV达到100,即调用防火墙命令封掉对应的IP,监控频率每隔5分钟。防火墙命令为:iptables -A INPUT -s IP -j REJECT

7、将以下文件内容中FQDN取出并根据其进行计数从高到低排序

http://mail.magedu.com/index.html
http://www.magedu.com/test.html
http://study.magedu.com/index.html
http://blog.magedu.com/index.html
http://www.magedu.com/images/logo.jpg
http://blog.magedu.com/20080102.html
http://www.magedu.com/images/magedu.jpg

参考答案:

[root@centos8 ~]#awk -F"/" '{url[$3]++}END{for(i in url){print url[i],i}}' 
url.log |sort -nr
3 www.magedu.com
2 blog.magedu.com
1 study.magedu.com
1 mail.magedu.com

8、将以下文本以inode为标记,对inode相同的counts进行累加,并且统计出同一inode中,
beginnumber的最小值和endnumber的最大值

inode|beginnumber|endnumber|counts|
106|3363120000|3363129999|10000|
106|3368560000|3368579999|20000|
310|3337000000|3337000100|101|
310|3342950000|3342959999|10000|
310|3362120960|3362120961|2|
311|3313460102|3313469999|9898|
311|3313470000|3313499999|30000|
311|3362120962|3362120963|2|

输出的结果格式为:

310|3337000000|3362120961|10103|
311|3313460102|3362120963|39900|
106|3363120000|3368579999|30000|

你可能感兴趣的:(#,Linix进阶)