Linux 之 awk 命令 骑猪看日落 2022-02-24 05:20 315阅读 0赞 **简单介绍** awk 是由 Alfred Aho 、Peter Weinberger、Brian Kernighan 这三个人创造的,所以 awk 名字是由这三个人姓氏的首字母组成。 awk 是一个报告生成器,拥有强大的文本格式化能力,我们可以利用 awk 命令,将一些文本处理成我们想要的样子,比如整理成 **" 表格 "** 的样子。 awk 是一个行编辑器; awk 其实是一门编程语言,支持条件判断,数组,循环等功能,所以 awk 也是一个脚本语言解释器; awk、sed、grep 并称为 Linux 三剑客,其中 grep 适合单纯的查找或匹配文本,sed 适合编辑匹配到的文本(特别是庞大的文本文件),而 awk 则长于对文本进行复杂的格式化处理; -------------------- **语法** awk [options] 'pattern{action}' file1,file2... awk [-F|-v] 'BEGIN{ commands } pattern{ commands } END{ commands }' file1,file2... -------------------- **options** 1) options省略时,以空白字符为分隔符; 2) -F 指定分隔符; 3) -v 设置变量; 变量分**内置变量**和**自定义变量**; 内置变量: FS:输入分隔符,默认为空白字符; OFS:输出分隔符,默认为空白字符; RS:输入换行符,指定输入时的换行符; ORS:输出换行符,输出时用指定符号代替换行符; NF:当前行的字段的个数,即当前行被分割成了几列; NR:行号,当前处理的文本行的行号; FNR:各文件分别计数的行号,awk处理多文件时,分别计数各文件行号; FILENAME:当前文件名; ARGV:数组,保存的是命令行所给定的各参数; ARGC:命令行参数的个数; 自定义变量可以在 options 中指定,也可以在 '\{ \}' 程序段中指定,区别是前者可以调用 shell 中的变量,然后传参给 '\{ \}' 程序段; 4) --posix 当 patter 部分使用正则模式的次数匹配时 \{m,n\},搭配使用该选项; -------------------- **pattern** pattern,模式,也叫条件,只要被 pattern 匹配到的文本行,都执行 action 部分的动作。 1) pattern 省略时,表示空模式,空模式会匹配文本当中的每一行,对每一行都执行 action 部分的动作; 2) BEGIN/END 模式 BEGIN{ commands } # 处理文本之前执行的动作; END{ commands } # 文本处理之后执行的动作; 3) 关系运算模式 > < == != >= <= ~ !~ ~ 和 !~ 搭配正则表达式进行模式匹配,eg: [root@Python ~]# df 文件系统 1K-块 已用 可用 已用% 挂载点 /dev/mapper/centos-root 17811456 8719660 9091796 49% / devtmpfs 914752 0 914752 0% /dev tmpfs 931624 0 931624 0% /dev/shm tmpfs 931624 10092 921532 2% /run tmpfs 931624 0 931624 0% /sys/fs/cgroup /dev/sda1 1038336 165968 872368 16% /boot tmpfs 186328 0 186328 0% /run/user/0 [root@Python ~]# df | awk '$6~/^\/run/{print $0}' tmpfs 931624 10092 921532 2% /run tmpfs 186328 0 186328 0% /run/user/0 4) 正则模式 pattern 部分正则模式匹配要放入两个斜线中,语法如下: awk [options] '/正则表达式/{action}' file 注1:如果正则表达式中包含 '/' 要使用 '\\' 进行转义; 注2:awk 默认使用扩展正则表达式; 注3:使用 \{m,n\} 进行次数匹配时需要使用 --posix 选项,不然会报错; 5) 行范围模式 语法: awk [options] '/正则1/,/正则2/{action}' file -------------------- **action** action,动作,分为两大类,输出语句和控制语句; **1、输出语句** 1) action 省略 如若省略 action,则表示被 pattern 匹配到的行都打印出来,及默认\{ print $0 \}; 2) print 简单打印 可打印字符串,也可将 pattern 匹配到的行打印至屏幕,同时可结合位置变量打印我们需要的字段至屏幕; 常用位置变量: $0 $1...$n $NF $(NF-1) $NF表示当前行的最后一个字段; 3) printf 格式化打印 printf() 是C语言中的一个函数,linux 的 printf 命令模仿了该函数,可以按照我们指定的格式输出文本;awk 工具 嵌入了 printf 命令,让 action 部分可以格式化打印文本; printf 核心功能为 格式替换符和转义字符; 格式替换符: - 左对齐 Width 域的步长,用0表示0步长 .prec 最大字符串长度,或小数点右面的位数 %c ASCII字符 %d 整型 %e 科学计数法 %f 浮点型 #使用小数点后2位%.2f (用于除法后) %g awk决定哪种浮点数转换e或者f %o 八进制 %s 字符串 %x 十六进制 转义字符: \n 换行 \r 回车 \t 水平制表符 \v 垂直制表符 \f 换页 \b backspace 删除前面一个字符,前提是\b后面要存在字符 \c 不显示输出结果中的换行字符 举个栗子: [root@syztoo ~]# awk -F: 'BEGIN{ printf "%-10s\t%s\n" , "用户名称","用户ID" } { printf "%-10s\t%s\n" , $1,$3 }' /etc/passwd 用户名称 用户ID root 0 bin 1 daemon 2 adm 3 lp 4 sync 5 shutdown 6 halt 7 mail 8 operator 11 games 12 ftp 14 nobody 99 systemd-network 192 dbus 81 polkitd 999 sshd 74 postfix 89 chrony 998 ntp 38 tcpdump 72 nscd 28 nginx 997 mysql 27 dockerroot 996 tss 59 syztoo 1000 **2、控制语句** 1) **if** 语法: awk [options] 'pattern{if(条件){输出语句}}' file 2) **if...else** 语法: awk [options] 'pattern{if(条件){输出语句}else{输出语句}}' file 3) **if...else if...else** 语法: awk [options] 'pattern{if(条件){输出语句}else if(条件){输出语句}else{输出语句}}' file 4) **for** for循环语法格式1: for(初始化; 布尔表达式; 更新) { 代码语句 } 栗子 : [root@syztoo ~]# awk 'BEGIN{ for(i=1; i<=5; i++) { print i} }' 1 2 3 4 5 for循环语法格式2: for( 变量 in 数组 ) { 代码语句 } 栗子: [root@syztoo ~]# awk 'BEGIN{arr[1]="dog";arr[2]="cat";arr[3]="pig"; for(i in arr){ print arr[i] }}' dog cat pig 5) **while** 语法: while( 布尔表达式 ) { 代码语句 } 栗子: [root@syztoo ~]# awk -v n=1 'BEGIN{ while (n<=5) {print n; n++}}' 1 2 3 4 5 [root@syztoo ~]# awk 'BEGIN{ n=2 ; while (n<=5) {print n; n++}}' 2 3 4 5 6) **do...while** 语法: do { 代码语句 } while( 条件 ) 7)**break,continue** awk 与其他编程语言一样,在 for 或者 while 循环中,也可以使用 break 和 continue 来跳出循环,break 跳出整个循环,continue 跳出本次循环。 栗子: [root@syztoo ~]# awk 'BEGIN{ n=1 ; while (n<=10) { if (n>=5) { break }; print n; n++ }}' 1 2 3 4 [root@syztoo ~]# awk 'BEGIN{ n=1; while(n<=10) { n++; if(n%2==1) { continue }; print n }}' 2 4 6 8 10 8)**exit** 在 shell 中,exit 表示退出当前脚本,在 awk 中,也可使用 exit,表示退出整个 awk 命令; 栗子: [root@syztoo ~]# awk 'BEGIN{ print 1 ; exit ; print 2 ; print 3 }' 1 -------------------- **数组** awk 中的数组不需要向 java 一样提前进行声明。 栗子: [root@syztoo ~]# awk 'BEGIN{animal[1]="dog";animal[2]="cat";animal[3]="pig";for(i in animal){print i,animal[i]}}' 1 dog 2 cat 3 pig awk 创造数组时,除了手动创建,也可直接使用系统中已经定义好的数组,通过 -v 变量传入即可。 还可以使用 awk 内置 split 函数,将一段文本按指定分隔符进行分割,然后将分割后的字段保存为一个数组,只不过该数组的索引不是从0开始,而是从1开始; 举个栗子: [root@syztoo ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/java/bin:/usr/local/java/jre/bin:/usr/local/maven/bin:/usr/local/python3/bin:/root/bin [root@syztoo ~]# awk -v x=$PATH 'BEGIN{split(x, path, ":"); for(i in path) {print i,path[i]}}' 4 /usr/bin 5 /usr/local/java/bin 6 /usr/local/java/jre/bin 7 /usr/local/maven/bin 8 /usr/local/python3/bin 9 /root/bin 1 /usr/local/sbin 2 /usr/local/bin 3 /usr/sbin 再举个实际应用的栗子,统计文本中某个 IP 地址出现的次数; [root@syztoo ~]# cat test.txt 192.168.1.1 192.168.1.4 192.168.1.2 192.168.1.4 192.168.1.2 192.168.1.3 192.168.1.3 192.168.1.3 [root@syztoo ~]# awk '{ip[$1]++} END{for(i in ip){print i,ip[i]}}' test.txt 192.168.1.1 1 192.168.1.2 2 192.168.1.3 3 192.168.1.4 2 -------------------- **内置函数** ** ** awk 中的内置函数大致分为算术函数、字符串函数、时间函数和其他函数等。 **1、算术函数** 最常用的有 rand 、srand 、int 函数。 [root@syztoo ~]# awk 'BEGIN{ print rand() }' 0.237788 [root@syztoo ~]# awk 'BEGIN{ print rand() }' 0.237788 [root@syztoo ~]# awk 'BEGIN{ print rand() }' 0.237788 **rand** 函数返回一个 0 到 1 之间的随机数,但使用 rand 函数时,需要配合 **srand** 函数,否则就会出现上面的执行结果。 [root@syztoo ~]# awk 'BEGIN{ srand(); print rand() }' 0.863585 [root@syztoo ~]# awk 'BEGIN{ srand(); print rand() }' 0.298565 [root@syztoo ~]# awk 'BEGIN{ srand(); print rand() }' 0.89975 如果想生成一个 0 到 10 之间的整数,可以将随机数 \* 10 然后用 **int** 函数取整即可。 [root@syztoo ~]# awk 'BEGIN{ srand(); print int(rand()*10) }' 9 [root@syztoo ~]# awk 'BEGIN{ srand(); print int(rand()*10) }' 8 [root@syztoo ~]# awk 'BEGIN{ srand(); print int(rand()*10) }' 5 **2、字符串函数** ** ** 常用的字符串函数有 gsub、sub、length、index、split 函数等。 [root@syztoo ~]# cat a.txt hell0 i am syzt00 what are y0u d0ing [root@syztoo ~]# awk '{ gsub("0","o",$1); print $0 }' a.txt hello i am syzt00 what are y0u d0ing [root@syztoo ~]# awk '{ gsub("0","o",$0); print $0 }' a.txt hello i am syztoo what are you doing [root@syztoo ~]# awk '{ gsub("0","o"); print $0 }' a.txt hello i am syztoo what are you doing **gsub** 函数接收三个参数,第一个是要替换的字符(可以使用正则),第二个要替换成什么字符,第三个是选择要替换的列(省略时,默认为 $0,全部替换)。 **sub** 函数与 gsub 函数类似,区别在于 gsub 函数会替换指定范围内所有符合条件的字符,而 sub 函数只会替换指定范围内第一次匹配到字符。 [root@syztoo ~]# cat a.txt hell0 i am syzt00 what are y0u d0ing [root@syztoo ~]# awk '{ gsub("0","o"); print $0 }' a.txt hello i am syztoo what are you doing [root@syztoo ~]# awk '{ sub("0","o"); print $0 }' a.txt hello i am syzto0 what are you d0ing **length** 函数可以获取字符串长度。 [root@syztoo ~]# awk '{ print $1,length($1) }' a.txt hell0 5 i 1 what 4 **index** 函数返回字符串的位置。 [root@syztoo ~]# cat a.txt hell0 i am syzt00 what are y0u d0ing [root@syztoo ~]# awk '{ print index($0,"0") }' a.txt 5 10 11 [root@syztoo ~]# awk '{ print index($0,"00") }' a.txt 0 10 0 [root@syztoo ~]# awk '{ print index($3,"00") }' a.txt 0 5 0 index 接受两个参数,第一个参数指定索引范围(表示整行时,$0 不能省略),第二个参数指定要查找的字符串。匹配机制是,没有匹配到返回 0,第一次匹配到就返回索引位置(不会再往后面匹配)。 **split** 函数可将字符串分隔,然后保存为数组。 [root@syztoo ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/java/bin:/usr/local/java/jre/bin:/usr/local/maven/bin:/usr/local/python3/bin:/root/bin [root@syztoo ~]# awk -v x=$PATH 'BEGIN{split(x, path, ":"); for(i in path) {print i,path[i]}}' 4 /usr/bin 5 /usr/local/java/bin 6 /usr/local/java/jre/bin 7 /usr/local/maven/bin 8 /usr/local/python3/bin 9 /root/bin 1 /usr/local/sbin 2 /usr/local/bin 3 /usr/sbin 注意 split 函数分隔后的数组索引是从 1 开始,split 函数也有返回内容,返回分隔后的数组长度。 [root@syztoo ~]# awk -v x=$PATH 'BEGIN{ print split(x, path, ":") }' 9 -------------------- **三元运算** ** ** 格式: condition ? result 1 : result 2 如果条件成立,执行 result 1 ,否则执行 result 2 栗子: [root@syztoo ~]# awk -F: '{ $3 < 1000? a++: b++ } END{ print a,b }' /etc/passwd 26 1 -------------------- **打印奇偶行** [root@syztoo ~]# cat a.txt 1 hello 2 i am syztoo 3 what are you doing 4 hi syztoo 5 i just got up 6 i will play basketball later 7 would you like to join me 8 ok 9 see you later 打印奇数行: [root@syztoo ~]# awk 'a=!a' a.txt 1 hello 3 what are you doing 5 i just got up 7 would you like to join me 9 see you later 打印偶数行: [root@syztoo ~]# awk '!(a=!a)' a.txt 2 i am syztoo 4 hi syztoo 6 i will play basketball later 8 ok --------------------
还没有评论,来说两句吧...