Linux命令之——awk

Linux命令之——awk

Scroll Down

其他文章:


awk 命令

awk 名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。

awk 是一种很棒的语言,最基本功能是在文件或者字符串中基于指定规则浏览和抽取信息,awk 抽取信息后,才能进行其他文本操作。
完整的 awk 脚本通常用来格式化文本文件中的信息。

通常,awk 是以文件的一行为处理单位的。awk 每接收文件的一行,然后执行相应的命令,来处理文本。

基本用法:

# 行匹配语句 awk '' 只能用单引号
awk '{[pattern] action}' {filenames}

尽管操作可能会很复杂,但语法总是这样。

  • pattern:表示 AWK 在数据中查找的内容(正则表达式,用斜杠括起来)。
  • action:是在找到匹配内容时所执行的一系列命令。
  • {}:花括号 不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。

==awk 会对输入文件中的每一行都执行一次脚本代码。==

简单分隔

# 打印每一行
awk '{ print $0 }' /etc/passwd
# 以 : 分隔,打印每行第一列
awk -F: '{ print $1 }' /etc/passwd
awk -F":" '{ print $1 }' /etc/passwd
# 以 : 分隔,打印每行第一列第三列
awk -F":" '{ print $1 $3 }' /etc/passwd
# 以 : 分隔,打印每行第一列(空格)第三列
awk -F":" '{ print $1 " " $3 }' /etc/passwd
# 以 : 分隔,打印每行第一列第三列(拼接字符串)
awk -F":" '{ print "username: " $1 "\t\tuid:" $3 }' 
/etc/passwd
# 以 : 分隔,打印每行第一列第三列,格式化输出(printf 不换行,故末尾加个\n)
awk -F":" '{printf "username: %-20s \t uid: %-5s\n",$1,$3}' /etc/passwd

-F参数:指定分隔符,可指定一个或多个,是一个字符串或者是一个正则表达式。
print后面做字符串的拼接。

只查看文件第5~10行的内容

# NR 就是行号,从 1 开始
awk '{if(NR>=5 && NR<=10) print $0}' /etc/passwd

过滤字符串

echo "I am Sherlock,my lastname is Holmes" > awk_01.txt
# 过滤出 名 和 姓,此处 -F 后指定了分隔符 正则表达式
awk -F '[ ,]+' '{print $3, $7}' awk_01.txt

BEGIN 和 END 模块

有时候可能需要在 awk 开始处理输入文件中的文本之前执行初始化代码。对于这种情况, awk 允许您定义一个 BEGIN 块,awk 在开始处理输入文件之前会执行 BEGIN 块,因此它是初始化 FS(字段分隔符)变量、打印页眉或初始化其它在程序中以后会引用的全局变量的极佳位置。
awk 还提供了 END 块,在处理了输入文件中的所有行之后执行这个块。通常, END 块用于执行最终计算或打印应该出现在输出流结尾的摘要信息。

统计/etc/passwd的账户人数

## count是自定义变量,之前的action{}里都是只有一个print
## 其实print只是一个语句,而action{}可以有多个语句,以;号隔开。
## count,虽然默认是0,但是妥当的做法还是初始化为0:
awk 'BEGIN {count=0;print "[start] user count is ",count} {count=count+1;print $0} END{print "[end] user count is ",count}' passwd

统计某个文件夹下的文件占用的字节数

# 表示文件大小的正是第5列,所以是 $5
ll | awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ",size}'

# 结果以M为单位显示
ll |awk 'BEGIN{size=0;} {size=size+$5;} END{print "[end]size is ",size/1024/1024,"M"}'  

awk 运算符

运算符描述
= += -= *= /= %= ^= **=赋值
?:C条件表达式
||逻辑或
&&逻辑与
~ 和 !~匹配正则表达式和不匹配正则表达式
< <= > >= != ==关系运算符
空格连接
+ -加,减
* / %乘,除与求余
+ - !一元加,减和逻辑非
^ ***求幂
++ --增加或减少,作为前缀或后缀
$字段引用
in数组成员

赋值运算符
a+5; 等价于 a=a+5; 其他同类。

awk 'BEGIN{a=5;a+=5;print a}'

逻辑运算符

# 判断表达式 a>2&&b>1为真还是为假,后面的表达式同理,输出:0 1
awk 'BEGIN{a=1;b=2;print (a>2&&b>1,a=1||b>1)}'

正则运算符

# 输出:ok
awk 'BEGIN{a="100testaa";if(a~/100/) {print "ok"}}'
# 输出:ok
echo|awk 'BEGIN{a="100testaaa"}a~/test/{print "ok"}'

算术运算符
算术运算符操作时,操作数自动转为数值,所有非数值都变为0。

# 输出:0, 2
awk 'BEGIN{a="b";print a++,++a}'
# 输出:20, 22
awk 'BEGIN{a="20b4";print a++,++a}'

关系运算法
> <可以作为字符串比较,也可以用作数值比较,关键看操作数如果是字符串就会转换为字符串比较。
两个都为数字 才转为数值比较。
字符串比较:按照ascii码顺序比较。

# 无输出
awk 'BEGIN{a="11";if(a>=9){print "ok"}}'
# 输出:ok
awk 'BEGIN{a=11;if(a>=9){print "ok"}}' 
# 输出:ok
awk 'BEGIN{a;if(a>=b){print "ok"}}'

# 过滤第4列大于1,并且第1列等于第5列的行
awk -F: '$4>1 && $1==$5 {print $1,$3,$4,$6}' /etc/passwd

三目运算符

# 输出:ok
awk 'BEGIN{a="b";print a=="b"?"ok":"err"}'
# 输出:err
awk 'BEGIN{a="b";print a=="c"?"ok":"err"}'

常用 awk 内置变量

变量描述
$n当前记录的第n个字段,字段间由FS分隔
$0完整的输入记录
ARGC命令行参数的数目
ARGIND命令行中当前文件的位置(从0开始算)
ARGV包含命令行参数的数组
CONVFMT数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组
ERRNO最后一个系统错误的描述
FIELDWIDTHS字段宽度列表(用空格键分隔)
FILENAME当前文件名
FNR各文件分别计数的行号
FS字段分隔符(默认是任何空格)
IGNORECASE如果为真,则进行忽略大小写的匹配
NF一条记录的字段的数目
NR已经读出的记录数,就是行号,从1开始
OFMT数字的输出格式(默认值是%.6g)
OFS输出记录分隔符(输出换行符),输出时用指定的符号代替换行符
ORS输出记录分隔符(默认值是一个换行符)
RLENGTH由match函数所匹配的字符串的长度
RS记录分隔符(默认是一个换行符)
RSTART由match函数所匹配的字符串的第一个位置
SUBSEP数组下标分隔符(默认值是/034)

内置变量概览

awk -F":" 'BEGIN{printf "%10s %8s %8s %6s %10s %4s %10s %10s %10s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS","ORS","RS";printf "文件名         参数个数 分计行号 分隔符 字段数目 行号 输出分隔符 输出分隔符  记录分隔符 \n";printf "------------------------------------------------------------------------------------------\n"} {printf "%5s %8s %8s %6s %8s %4s %10s %10s %10s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS}'  /etc/passwd

字段分隔符 FS

FS="\t+"一个或多个 Tab 分隔

awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' tab.txt

FS="[[:space:]+]"一个或多个空白空格(默认的

echo 'we are  studing awk now!' > space.txt
awk -F [[:space:]+] '{print $1,$2,$3,$4,$5}' space.txt
awk -F [[:space:]+] '{print $1,$2}' space.txt

FS="[" ":]+"以一个或多个空格或 : 分隔

awk -F [" ":]+ '{print $1,$2,$3}' /etc/passwd

字段数量 NF

awk -F ":" 'NF==7{print $0}' /etc/passwd

记录数量 NR

获取网卡 ip 地址(取第2行)

ifconfig eth0|awk -F [" ":]+ 'NR==2{print $3}'

RS 记录分隔符变量

将 FS 设置成"\n"告诉 awk 每个字段都占据一行。
通过将 RS 设置成 "",还会告诉 awk 每个地址记录都由空白行分隔。

echo 'Jimmy the Weasel
100 Pleasant Drive
San Francisco,CA 123456

Big Tony
200 Incognito Ave.
Suburbia,WA 64890' > recode.txt

echo '#!/bin/awk
BEGIN {
        FS="\n"
        RS=""
}
{
        print $1 " | " $2 " | " $3
}' > awk.txt
# Jimmy the Weasel,100 Pleasant Drive,San Francisco,CA 123456
# Big Tony,200 Incognito Ave.,Suburbia,WA 64890
awk -f awk.txt recode.txt

OFS 输出字段分隔符

awk 'BEGIN{FS=":"}{print $1 "," $2 "," $3}' /etc/passwd
awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' /etc/passwd

ORS 输出记录分隔符

echo '#!/bin/awk
BEGIN {
        FS="\n"
        RS=""
        ORS="\n\n"
}
{
        print $1 "|" $2 "|" $3
}' > awk.txt
awk -f awk.txt recode.txt

awk 正则

规则表达式

awk '/REG/{action}' file
/REG/为正则表达式,可以将$0中满足条件的记录送入到action进行处理。

# 匹配所有包含root的行
awk '/root/{print $0}' /etc/passwd
# 以冒号作为分隔符,匹配第5个字段是 root 的行
awk -F: '$5~/root/{print $0}' /etc/passwd
# 获取网卡 ip 地址(取第2行)
ifconfig eth0|awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print $3}'

列字符串匹配

# 输出第二列包含 "th",并打印第二列与第四列
$ awk '$2 ~ /th/ {print $2,$4}' log.txt

~ 表示模式开始。// 中是模式。

忽略大小写

awk 'BEGIN{IGNORECASE=1} /this/' log.txt

模式取反
匹配不以th开头的行

awk '$2 !~ /th/ {print $2,$4}' log.txt
# 或者
awk '!/th/ {print $2,$4}' log.txt

布尔表达式

awk '布尔表达式{action}' file
仅当对前面的布尔表达式求值为真时, awk 才执行代码块。

# 以冒号作为分隔符,第1个字段(第一列)是 root 的行
awk -F: '$1=="root"{print $0}' /etc/passwd
# 以冒号作为分隔符,第1、5个字段(第一、五列)是 root 的行 
awk -F: '($1=="root")&&($5=="root") {print $0}' /etc/passwd

其他用法

一些实例

计算文件大小

ls -l *.txt | awk '{sum+=$6} END {print sum}'

从文件中找出长度大于80的行

awk 'length>80' log.txt

打印九九乘法表

seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'