awk 命令
介绍
AWK是一种处理文本文件的语言,是一个强大的文本分析工具,之所以叫AWK是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。
语法
- 第一种形式
1 | $ awk 'BEGIN{}pattern{commands}END{}' file |
- 第二种形式
1 | $ standard output | 'BEGIN{}pattern{commands}END{}' |
工作原理

其中数字1,2,3…代表是文本中的数据行
BEGIN{} 表示在处理文本数据之前进行一些操作
pattern{commands} 表示从文本中的第一行开始匹配满足pattern规则的行,执行commands命令,直到文本最后一行
END{} 表示在处理文本数据之后进行一些操作
内建变量
| 内置变量 | 含义 |
|---|---|
| $0 | 整行内容 |
| $1-$n | 当前行的第1-n个字段 |
| NF | 当前行的字段个数,也就是有多少列 |
| NR | 当前的行号,从1开始计数 |
| FNR | 各文件分别计数的行号 |
| FS | 字段分隔符(默认以空格或tab键分割) |
| RS | 输入行分隔符。默认回车换行 |
| OFS | 输出字段分隔符。默认为空格 |
| ORS | 输出行分隔符。默认为回车换行 |
| FILENAME | 当前文件名 |
内置变量如何使用,我们以文本score.txt内容来做演示
1 | $ cat score.txt |
使用$0来打印输出文件的每一整行的内容
1 | $ awk '{print $0}' score.txt |
注意:行匹配语句 awk ‘ ‘ 只能用单引号
使用$1、$3来打印输出文件的的每一行第1列和第3列内容
1 | $ awk '{print $1,$3}' score.txt |
使用NF来输出每一行的列数
1 | $ awk '{print NF}' score.txt |
使用awk没有指定分隔符,默认是以空格或者tab键来作分隔,在score.txt 文件中,经过分隔,每行都有5列
使用变量$NF来输出文件
1 | $ awk '{print $NF}' score.txt |
从输出结果我们总结出,$NF就表示每一行的最后一列,因为文件中每一行有5列,则 $NF = $5
之前我们打印每一行的第1列和第3列我们使用的是:$1,$3;那么现在我们也可以使用:$(NF-2),$(NF-4)
1 | $ awk '{print $(NF-4),$(NF-2)}' score.txt |
使用NR来输出每一行的行号,这次我们把每一行的内容也打印出来
1 | $ awk '{print NR "\t" $0}' score.txt |
使用ORS来指定输出每一行的指定的分隔符
1 | $ awk 'BEGIN{ORS="---"} {print $1,$NF}' score.txt |
指定了每一行的分隔符为”—“,现在输出的每一行都用—做分隔,而不在是默认的回车换行做分隔
使用ORS指定了行的分隔符为”—“,但是现在我们看到每一行输出的分隔符还是默认的空格来做分隔,比如:输出第一行的 marry 14,现在我们使用OFS来指定输出字段的分隔符,比如:*号
1 | $ awk 'BEGIN{ORS="---";OFS="*"} {print $1,$NF}' score.txt |
使用FS指定每一行的字段分隔符
1 | $ awk 'BEGIN{FS=":"} {print $1,$NF}' /etc/passwd |
也可以使用这种形式指定分隔符(-F的意思就是指定分隔符)
1 | $ awk -F: '{print $1,$NF}' /etc/passwd |
如果指定多个分隔符,使用中括号[]包起来,里面指定具体的分隔符
1 | $ awk -F '[-:]' |
使用FILENAME在处理多文件时候,可以打印出当前的文件名
1 | $ awk 'BEGIN{FS="[ :]"} {print FILENAME "\t" $1,$NF}' score.txt /etc/passwd |
使用重定向拆分文件
准备文件 netstat.txt
1 | $ cat netstat.txt |
以空格为分隔符,按照$6(第6列)也就是State列进行拆分文件netstat.txt(其中NR!=1表示不处理表头)
1 | $ awk 'NR!=1{print > $6}' netstat.txt |
{print > $6}是按照第6列进行拆分,将所有列输出到文件中,我们也可以指定列输出到文件中
1 | $ awk 'NR!=1{print $1,$4> $6}' netstat.txt |
awk使用printf格式化输出
printf 格式说明符
| 格式符 | 含义 |
|---|---|
| %s | 打印字符串 |
| %d | 打印十进制数 |
| %f | 打印一个浮点数 |
| %x | 打印十六进制数 |
| %o | 打印八进制数 |
| %e | 打印数字的科学计数法形式 |
| %c | 打印单个字符的ASCII码 |
printf 修饰符
| 修饰符 | 含义 |
|---|---|
| - | 左对齐 |
| + | 右对齐 |
| # | 8进制显示在前面加0,16进制显示在前面加0X |
上面演示的案例,我们都是用print来做输出(默认是输出每行内容,用回车键作为换行),现在我们使用printf(默认没有加任何的输出分隔符)来进行格式化打印输出
使用%s以字符串形式输出/etc/passwd文件中第一列
1 | $ awk -F: '{printf "%s\n",$1}' /etc/passwd |
使用
\n来指定输出每行的的分隔符
使用$s、%f、%d、%x等多个格式符来输出
1 | $ awk -F: '{printf "%s %f %d %x\n",$1,$3,$3,$3}' /etc/passwd |
使用了不同的格式符,输出了/etc/passwd文件中的$1,$3列,对于稍微有点强迫症患者的朋友来说,可以看出输出结果并不美观,比如:每列都没有对齐(向左或者向右),浮点数列没有控制指定个数的精度等,下面我们就来解决这个问题
1 | $ awk -F: '{printf "%-20s %-0.2f \t\t %-20d %-20x\n",$1,$3,$3,$3}' /etc/passwd |
我们可以在每个格式符的前面加上数字,比如:%20s,表示这一列占位20个字符
使用 %0.2f 来指定这一列输出的精度,保留2位小数
使用
-格式符,表示输出的结果向左对齐
关系运算符、布尔运算符和匹配正则表达式
| 运算符 | 含义 | ||
|---|---|---|---|
| <、 <=、 >、 >=、 != 、 == | 小于、小于等于…(关系运算符) | ||
| ~ | 匹配正则表达式 | ||
| ~! | 不匹配正则表达式 | ||
| \ | \ | 逻辑或 | |
| && | 逻辑与 | ||
| ! | 非 |
匹配输出第一列字符等于smile的行
1 | $ awk '$1=="smile" {print $0}' score.txt |
匹配输出第一列字符等于smile的行 ,或者第2列大于 80的行
1 | $ awk '$1=="smile" || $2 > 80 {print $0}' score.txt |
使用正则,匹配输出包含44的行
1 | $ awk '/44/ {print $0}' score.txt |
使用正则,匹配输出不包含44的行
1 | $ awk '!/44/ {print $0}' score.txt |
使用正则,匹配输出以join字符开头的行到以jack字符开头结束的行
1 | $ awk '/^join/,/^jack/ {print $0}' score.txt |
使用正则,匹配第1列字符包含jack并且最后一列大于等于34
1 | $ awk '$1 ~ /jack/ && $NF >= 34 {print $0}' score.txt |
~ 表示模式开始;/ / 中表示的是匹配的具体模式
现在看下正则模式取反的示例
1 | $ awk '$1 !~ /jack/ && $NF >= 24 {print $0}' score.txt |
!~,第1列不包含字符jack并且最后列的值大于等于24
算术运算符
| 运算符 | 含义 |
|---|---|
| + 、 - | 加、减 |
| * 、/ 、 % | 乘、除、求于 |
| ++、– | 增加或减少,作为前缀或后缀,如(++x、x–) |
我们先来看一下简单的加法和除法运算
1 | $ awk 'BEGIN{ |
awk 声明的变量可以在任何多个花括号脚本中使用
计算/etc/services中空白行的数量
1 | $ awk '/^$/ {sum++} END{print sum}' /etc/services |
字符串函数
| 函数名 | 含义 |
|---|---|
| length(str) | 计算字符串长度 |
| index(str1,str2) | 在str1中查找str2的位置 |
| tolower(str) | 转换为小写 |
| toupper(str) | 转换为大写 |
| substr(str,m,n) | 从str的第m个字符开始,截取m位 |
| split(str,arr,fs) | 按fs切割字符,结果保存arr中 |
| match(str,RE) | 在str中按照RE查找 |
由于字符串函数是我们工作中常用的函数,比较简单,下面我们使用split()函数来做演示
1 | $ awk 'BEGIN{ |
其中我们使用了for循环来遍历 arr 数组,下面我们会讲到
awk动作中使用条件及循环语句(可见awk是个脚本解释器)
条件表达式
- if语句
语法格式如下:
1 | if(条件){ |
示例:
1 | $ awk -F: '{if($3>100 && $3<1000) print $0}' /etc/passwd |
- if - else 语句
语法格式如下:
1 | if(条件){ |
示例:
1 | $ awk 'BEGIN { |
- if - else - if 语句
语法格式如下:
1 | if(条件){ |
示例:
1 | $ awk -F: '{ |
循环语句
- while 语句
1 | while(条件){ |
示例:
1 | $ awk 'BEGIN {i=1; while(i<6){ print i; ++i } }' |
- do - while语句
语法格式:
1 | do{ |
示例:
1 | awk 'BEGIN{i=1;do{print i;i++}while(i<6)}' |
- for 语句
语法格式:
1 | for(变量;条件;表达式){ |
示例:
1 | $ awk 'BEGIN{ |
常用选项
| 选项 | 含义 |
|---|---|
| -v | 参数传递 |
| -V | 查看awk的版本号 |
| -f | 指定脚本文件 |
| -F | 指定分隔符 |
使用-v引入外部变量
1 | $ a=1 |
把awk的执行动作写在脚本中,使用-f指定脚本文件
1 | $ vim print.awk |
执行脚本文件:print.awk
1 | awk -v c="$a" -v d="$b" -f print.awk |
使用-F指定分隔符
我们之前可以使用内置变量FS来指定分隔符,比如:
1 | $ awk 'BEGIN{FS=":"} {print $1}' /etc/passwd |
现在等价于:
1 | $ awk -F ":" '{print $1}' /etc/passwd |