Shell(1):awk
0、常用
1、第一列 awk '{print $1}' filename 2、前两列(用空格分隔) awk '{print $1,$2}' filename 3、打印完第一列,然后打印第二列(无分隔) awk '{print $1 $2}' filename #2和3的区别在于,2的列与列输出时会有间隔,3的列与列在输出时直接拼在一块 4. 最后一列 awk '{print $NF}'#$NF:最后一列的值;NF:列数 5、总行数 总列数: awk 'END{print NR}' filename awk 'END{print NF}' filename 总列数(由于行与行的列数可能不同) 因此这里输出的是最后一行的列数 1、第一行 : awk 'NR==1{print}' filename 2. 第一行的1、2、3列 awk 'NR==1{print $1, $2, $3}' 3. 指定分隔符(这里以:分割) awk -F':' '{print $1}' 4、第5~9行,第1,2,3列 awk -F':' 'NR>=5 && NR<=9{print $1,$2,$3}' 5、第5~9行,全部列 awk -F':' 'NR>=5&&NR<=9{for(i=1;i<=NF;i++) printf("%s ",$i);printf("\n")} }' #注意这里的printf("\n")与前边的printf在同一个大括号内, #前边的大括号每行会循环NF次,而printf"\n"则每行执行一次,它和for循环用;分隔,是并列的,不参与循环 6、将5中的内容输出到文件test1 awk -F':' 'NR>=5&&NR<=9{for(i=1;i<=NF;i++) printf("%s ",$i);printf("\n")} }' test | awk 'print > "test1"' 9. 超出范围不报错 ps -aux | grep watchdog | awk '{print $100}'
1、概述
awk是一种处理文本文件的语言,是一个强大的文本分析工具。
Linux中的指令输出多是分列、分行的,例如常见的ll(列出路径下的文件)
ll 总用量 68 -rw-r--r--. 1 root root 1077 8月 10 2023 fastcgi.conf -rw-r--r--. 1 root root 1077 8月 10 2023 fastcgi.conf.default -rw-r--r--. 1 root root 1007 8月 10 2023 fastcgi_params -rw-r--r--. 1 root root 1007 8月 10 2023 fastcgi_params.default -rw-r--r--. 1 root root 2837 8月 10 2023 koi-utf -rw-r--r--. 1 root root 2223 8月 10 2023 koi-win -rw-r--r--. 1 root root 5231 8月 10 2023 mime.types -rw-r--r--. 1 root root 5231 8月 10 2023 mime.types.default -rw-r--r--. 1 root root 2656 8月 10 2023 nginx.conf -rw-r--r--. 1 root root 2656 8月 10 2023 nginx.conf.default -rw-r--r--. 1 root root 636 8月 10 2023 scgi_params -rw-r--r--. 1 root root 636 8月 10 2023 scgi_params.default -rw-r--r--. 1 root root 664 8月 10 2023 uwsgi_params -rw-r--r--. 1 root root 664 8月 10 2023 uwsgi_params.default -rw-r--r--. 1 root root 3610 8月 10 2023 win-utf
如果想提取这些列中的某一列进行处理,就可以用awk:
ll | awk '{print $5}' 1077 1077 1007 1007 2837 2223 5231 5231 2656 2656 636 636 664 664 3610
这里相当于用awk提取出第五列。
从这里可以对awk的作用进行一个简述,对那些按行列传入的数据,可以对某一列进行一个统一的处理。
2、awk基本使用
1)用法
awk '条件1{动作1} 条件2{动作2}...' 文件
awk 参数1 变量1 参数2 变量2 '条件1 {动作1} 条件2 {动作2}' 文件/内容
说明
- awk必须有输入文件或者标准输入,也就是说要么在最后写文件名,要么用管道|将要处理的内容送进来,不能单放一个awk '...'上来
awk '条件 {动作}' 文件名
#或 echo 输出内容 | awk '条件 {动作}' - 如果要进行awk测试(也就是说不需要依赖输入文件的数据),可以这样写:
echo | awk '测试内容'
2)条件
一般使用关系表达式:
- x > 10 判断变量x是否大于10
- x == y 判断变量x是否等于变量y
- A ~ B 判断字符串A中是否包含能匹配B表达式的子字符串
- A !~ B 判断字符串A中是否不包含能匹配B表达式的子字符串
3)动作:①格式化输出printf;②流程控制语句
printf用法
printf '输出类型输出格式' 内容
输出类型:以%开头
输出类型 |
说明 |
%c | ASCII字符 |
%s | 字符串(后边内容中有几个参数就要写几个%s) |
%-ns | 字符串(-表示左对齐,n表示输出几个字符) |
%-ni | 输出整数,n代表输出几个数字 |
%f | 小数点右边的位数 |
%m.nf | 浮点数,m代表全部位数,n代表小数位数 |
输出特殊格式(换行、占位):以\开头
输出格式 |
说明 |
\a | 输出警告声音 |
\b | 输出退格键 |
\f | 清除屏幕 |
\n | 换行 |
\r | 回车 |
\t | 水平输出TAB |
\v | 垂直输出TAB |
内容
内容如果是个文件,则会按行输出每行的处理结果
例子
①常规打印
[root@localhost ~]$ vi student.txt
ID Name php Linux MySQL Average
1 AAA 66 66 66 66
2 BBB 77 77 77 77
3 CCC 88 88 88 88
#printf格式输出文件
[root@localhost ~]$ printf '%s\t %s\t %s\t %s\t %s\t %s\t \n’ $(cat student.txt)#printf '格式' 内容
#%s分别对应后面的参数,6列就写6个
ID Name php Linux MySQL Average
1 AAA 66 66 66 66
2 BBB 77 77 77 77
3 CCC 88 88 88 88
②输出到文件
echo | awk '{print "hello world!\n" >> "test"}'
三个注意点:
- awk必须要有输入文件或者管道输入项,如果不需要,可以写出 echo | awk '...'的形式;
- 输出到文件,这个文件名需要用双引号""括起来;
- 输出到文件,写法为>>(追加)和>(覆盖),即输出重定向。
4)常用参数(放在awk之后)
- -F 指定输入时用到的字段分隔符
- -v 自定义变量
- -f 从脚本中读取awk命令
- -m 对val值设置内在限制
①用法:awk 参数1 变量1 参数2 变量2 '条件1 {动作1} 条件2 {动作2}' 文件/内容
②例子
awk -F':' -v ORS="," '{$1=$1;PRINT $0}' /etc/passwd
解释:
分隔符-F为:
输出结果分隔符(项与项之间)-v为ORS=",",
动作为将第一行赋值给第一行,打印每一行
内容为 /etc/passwd的每一行
③awk内置变量(-v后可以跟的内容)
awk内置变量 |
作用 |
$0 | 目前awk读入的整行数据 |
$n | 目前读入行的第n个字段(列) |
NF | 当前行拥有的字段(列)数 |
NR | 当前awk处理的是哪一行 |
FS |
输入文件的分隔符(和-F作用相同,-v FS=':'的作用就和-F ':'相同)。 默认的是任何空格,如果想要用其他分隔符(如":"),就需要用FS变量定义,写法 -v FS=':' |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
FNR | 当前文件中的当前记录数(对输入文件起始为1) |
OFMT | 数值的输出格式(默认%.6g) |
OFS | 输出字段(列与列之间)的分隔符(默认空格) |
ORS | 输出记录(行与行之间)的分隔符(默认换行符) |
RS |
输入记录的分隔符(默认换行符) 这个变量的含义是,原文件中每条记录之间的分隔符,默认情况下是一行为一条记录,此时记录之间的分隔符就是换行符 |
记录:各行
字段:各列
补充:
如果有多个自定义变量,就要为每个变量配一个-v
#指定输入列与列分隔符为:,输出记录(行与行)分隔符为,
awk -v FS=':' -v ORS="," '{$1=$1;PRINT $0}' /etc/passwd
5)awk条件
awk 参数1 变量1 参数2 变量2 '条件1 {动作1} 条件2 {动作2}' 文件/内容
条件类型 |
条件 |
说明 |
awk保留字 | BEGIN |
在awk程序开始时,尚未读取任何数据之前执行。 BEGIN后的动作只在程序开始时执行一次。 |
END |
在awk程序处理完所有数据,即将结束时执行。 END后的动作只在程序结束时执行一次。 |
|
关系运算符 | > | |
< | ||
>= | ||
<= | ||
== | ||
!= | ||
A~B | A中包含B的子字符串 | |
A!~B | A中不包含B的子字符串 | |
正则表达式 | /正则/ |
①BEGIN
BEGIN是awk保留字,是一种特殊的条件类型。BEGIN的执行时机是“awk程序一开始,尚未读取任何数据之前执行”。
BEGIN定义的动作只能被执行一次(执行一次意思是不再像普通语句那样对每行都循环执行,而是在所有行之前执行一次就结束):
awk 'BEGIN{printf "This is a transcript \n" } {printf $2 "\t" $6 "\n"}’ student.txt
#awk命令只要检测不到完整的单引号不会执行,所以这个命令的换行不用加入“|”,就是一行命令
#这里定义了两个动作
#第一个动作使用BEGIN条件,所以会在读入文件数据前打印“这是一张成绩单”(只会执行一次)
#第二个动作会打印文件的第二字段和第六字段
②END
也是awk保留字,但和BEGIN相反。END是awk程序处理完所有数据,即将结束时执行一次(不管这个END及其后的语句放在哪里,都是在最后一行执行完毕后执行):
awk 'END{printf "The End \n"} {printf $2 "\t" $6 "\n"}’ student.txt #在输出结尾输入“The End”,这并不是文档本身的内容,而且只会执行一次
③关系运算符
例①平均成绩大于等于87分的学员是谁
cat student.txt | grep -v Name | awk '$6 >= 87 {printf $2 "\n"}'
#使用cat输出文件内容,用grep取反包含“Name”的行
#判断第六字段(平均成绩)大于等于87分的行,如果判断式成立,则打第六列(学员名$2)
加入条件之后,只有条件成立,动作才会执行。通过这个例子可以发现:awk虽然是列提取指令,但是要按行来读入。它的执行过程如下:
- 有BEGIN条件,先执行BEGIN定义的动作;
- 如果没有BEGIN条件,则读入第一行,把第一行的数据依次赋予$0、$1、$2等变量。其中$0代表该行整体数据,$1代表第一字段,$2代表第二字段;
- 根据条件判断动作是否执行。如果条件符合,则执行;否则读入下一行数据。如果没有条件,则每行都执行动作。
- 读入下一行数据,重复上述步骤。
例②查找Sc用户的平均成绩
awk '$2 ~ /Sc/ {printf $6 "\n"}' student.txt
#如果第二字段中输入包含有“Sc”字符,则打印第六字段数据
85.66
在awk中,使用"//"包含的字符串,awk命令才会查找。也就是说字符串必须用"//"包含,awk命令才能正确识别。(如上文的/Sc/)
④正则表达式
如果要让awk识别字符串,必须用"//"包含,
例1
awk '/Liming/ {print}’student.txt #打印Liming的成绩
例2:使用df命令查看分区使用情况,如何只想查看真正的系统分区的使用情况,而不想查看光盘和临时分区的使用情况:
df -h | awk '/sda[O-9]/ {printf $1 "\t" $5 "\n"}’ #查询包含有sda数字的行,并打印第一字段和第五字段
6)awk常用实例统计
awk 参数1 变量1 参数2 变量2 '条件1 {动作1} 条件2 {动作2}' 文件/内容
1、第一列 awk '{print $1}' filename 2、前两列(用空格分隔) awk '{print $1,$2}' filename 3、打印完第一列,然后打印第二列(无分隔) awk '{print $1 $2}' filename
#2和3的区别在于,2的列与列输出时会有间隔,3的列与列在输出时直接拼在一块
4. 最后一列
awk '{print $NF}'#$NF:最后一列的值;NF:列数
5、总行数 总列数: awk 'END{print NR}' filename
awk 'END{print NF}' filename
总列数(由于行与行的列数可能不同)
因此这里输出的是最后一行的列数
1、第一行 : awk 'NR==1{print}' filename 2. 第一行的1、2、3列 awk 'NR==1{print $1, $2, $3}' 3. 指定分隔符(这里以:分割) awk -F':' '{print $1}'
4、第5~9行,第1,2,3列
awk -F':' 'NR>=5 && NR<=9{print $1,$2,$3}'
5、第5~9行,全部列
awk -F':' 'NR>=5&&NR<=9{for(i=1;i<=NF;i++) printf("%s ",$i);printf("\n")} }'
#注意这里的printf("\n")与前边的printf在同一个大括号内,
#前边的大括号每行会循环NF次,而printf"\n"则每行执行一次,它和for循环用;分隔,是并列的,不参与循环
6、将5中的内容输出到文件test1
awk -F':' 'NR>=5&&NR<=9{for(i=1;i<=NF;i++) printf("%s ",$i);printf("\n")} }' test
| awk 'print > "test1"'
9. 超出范围不报错 ps -aux | grep watchdog | awk '{print $100}'
7)awk定义和调用变量(放在动作{}块中)
awk 参数1 变量1 参数2 变量2 '条件1 {动作1} 条件2 {动作2}' 文件/内容
假设我想统计PHP成绩的总分,可以写为:
[root@localhost ~]$ awk 'NR==2 {php1=$3}
NR==3 {php2=$3}
NR==4 {php3=$3;total=phpl+php2+php3;print "total php is " total}' student.txt
#统计PHIP成绩的总分
对这个命令的解释:
-
'NR==2 {php1=$3}':如果输入数据是第二行(第一行是标题行),就把第二行的第三字段的值赋予变量php1;
-
'NR==3 {PHP2=$3}':同上
-
'NR==4 {php3=$3;total=php1+php2+php3;print "total php is " total}' student.txt:如果输入数据是第四行,就把第四行第三字段的值赋予php3,之后定义变量total的值为"php1+php2+php3",然后输出"total php is",后边加上变量total的值。
在awk编程中,因为命令语句很长,在输入格式时需要注意的内容:
- 多个条件 {动作}(即上文的NR==2 {...} 与 NR==3 {...})可以用空格分割,也可以用回车分割;
- 在一个动作(即上段代码的{...}部分)中,如果需要执行多个命令(即上文的php3=$3;total=...;),需要用";"分割,或者用回车分割;
- 在awk中,变量的赋值和调用都不需要加入$符(只有在调用某行的哪列时才会用到$n);(有点类似Python的语法)
8)流程控制(用if的话要放在动作块{}中,不用if就直接写成相应条件)
awk 参数1 变量1 参数2 变量2 '条件1 {动作1} 条件2 {动作2}' 文件/内容
之前所说awk用法:awk '条件1{动作1} 条件2{动作2}...' 文件
这里的流程控制是放在动作中(即大括号{}内)的
awk '{if (NR>=2) {if ($4>60) printf $2 "is a good man!\n"}}' student.txt
#程序中有两个if判断,第一个判断行号大于2,第二个判断Linux成绩大于90分
Liming is a good man !
Sc is a good man !
注意
awk{动作}中的if判断语句,完全可以利用awk自带的条件来取代,刚刚的脚本可以写作:
awk ’NR>=2 {test=$4}
test>90 {printf $2 "is a good man! \n"}’ student.txt
#先判断行号如果大于2,就把第四字段赋予变量test
#在判断如果test的值大于90分,就打印好男人
Liming is a good man!
Sc is a good man!
8)awk函数
定义(在动作块{}中)
function 函数名 (参数列表) {
函数体
}
#实际写法
awk 'function 函数名(参数列表){
函数体
}
{调用该函数}
' 文件/内容
例子
#定义函数test,包含两个参数,函数体的内容是输出这两个参数的值
awk 'function test(a,b) { printf a "\t" b "\n"}
{ test($2,$6) } ' student.txt
#调用函数test,并向两个参数传递值。
Name Average
AAA 87.66
BBB 85.66
CCC 91.66
9)awk调用外部脚本文件
对于小的单行程序而言,将脚本作为命令行自变量传递给awk是非常简单的,而对于多行程序就比较难以处理。当程序是多行时,使用外部脚本是很适合的。
首先在外部文件中写好脚本,然后可以使用awk的-f选项,使其读入脚本并且执行。
例子
首先写好一个awk脚本
vi pass.awk
BEGIN {FS=":"}
{ print $1 "\t" $3}
然后可以使用"-f"来调用这个脚本:
[root@localhost ~]$ awk -f pass.awk /etc/passwd
rooto
bin1
daemon2
…省略部分输出…
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性