其他文章:
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")}'
评论区