# awk
# 定义
awk 是一个文本处理工具,通常用于处理数据并生成结果报告
awk 的命名方式是它的三个创始人的首字母组成的
# 语法格式
语法格式 | 含义 |
---|---|
BEGIN{} | 表示在正式处理文本之前执行的命令 |
pattern | 匹配模式 |
{command} | 处理命令,可能多行,通过分号隔开。可执行变成命令比如 for 循环 |
END{} | 处理文本之后的执行命令 |
# 第一种
awk 'BEGIN{}pattern{command}END{}'file_name
# 第二种
standard output | BEGIN{}pattern{command}END{}
# 第三种(引用外部 awk)
- 在 vscode 中可以安装 awk 高亮工具使得-f 的时候单独使用.awk 文件代码高亮
执行 xxx.awk 文件
awk -f xxx.awk
或者 执行 xxx.awk 中的语句处理 aaa.txt
awk -f xxx.awk aaa.txt
# 常用选项
- 推荐-v 引入变量的时候都用双引号包起来
选项 | 解释 |
---|---|
-v | 参数传递(不同与 sed,awk 是不能够直接引用变量的) |
-f | 指定执行脚本文件 |
-F | 指定分隔符 |
-V | 查看 awk 版本号(Linux) |
--version | 查看 awk 版本号(Macos) |
var1=1
var2=2
awk -v num1=$var1 -v num2=$var2 'BEGIN{print num1,num2}'
1 2
var1=1
var2="hello world"
# 注意如果外部变量是字符串并且中间有空格那么-v的时候要用双引号引起来
awk -v num1=$var1 -v num2="$var2" 'BEGIN{print num1,num2}'
# 直接执行string.awk中的awk命令
awk -f string.awk
# 指定string.awk里面的awk命令对执行awk-string.txt
awk -f string.awk awk-string.txt
# 使用F指定分隔符
awk -F ":" '{print $1}' test1.txt
awk -F : '{print $1}' test1.txt
awk -F: '{print $1}' test1.txt
# 等价于
awk 'BEGIN{FS=":"}{print $1}' test1.txt
# 内置变量
⚠️macos 与 Linux 中有很多表现不一致的地方,以实际操作表现为准。可能性太多,无法完全记录
小技巧: print 本身是带换行的
# 内置变量对照表
{print $1,$3}是指输出第一个和第三个而不是 1-3
$NF通常表示每一行最大的字段,NF代表每一行的个数,那么$NF 表示第 NF 个
内置变量 | 含义 |
---|---|
$0 | 整行内容 |
$1-$n | 当前行的第 1-n 个字段 |
NF(Number Field) | 当前行的字段个数,也就是有多少列 |
NR (Number Row) | 当前行的行号,从 1 开始计数 |
FNR | 多文件处理时,每个文件的行号单独计数 |
FS(Field separator) | 输入字段分隔符。不指定默认以空格或者 tab 键划分 |
RS | 输入行分隔符。默认回车换行\n |
OFS(output FS) | 输出字段分隔符。默认为空格 |
ORS | 输入行分隔符。默认回车换行 |
FILENAME | 当前输入的问文件名字 |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
test1.txt
123:777
456:888
789 asd
# 输出所有内容
awk '{print $0}' test1.txt
# 以:为分隔符,输出每一行的第一个字段
awk 'BEGIN{FS=":"}{print $1}' test1.txt
123
456
789 asd
# 输出各行字段个数
awk '{print NF}' test1.txt
# 输出第NF个分割的的变量的值
awk '{print $NF}' test1.txt
# 输出两个文件的行号(累加)
awk '{print NR}' test1.txt test2.txt
# 输出两根文件的行号(不累加)
awk '{print FNR}' test1.txt test2.txt
test1.txt
- ⚠️ macos 下面操作完成后可能会多出末尾空行或者间隔空行,可以使用 grep 等方式去除
1|2|3--4|5|6--7|8|9
# 以--为换行表示分割文本内容,并忽略空行(可能会造成空行)
awk 'BEGIN{RS="--"}{print $0}' test1.txt | grep -v "^$"
1|2|3
4|5|6
7|8|9
# 以-为行分隔符并以|为字符分割输出第一个
awk 'BEGIN{RS="--";FS="|"}{print $1}' test1.txt | grep -v "^$"
1
4
7
# 输入同上,输出以^为行分隔符
awk 'BEGIN{RS="--";FS="|";ORS="^"}{print $1}' test1.txt | grep -v "^$"
test1.txt
ljc:cw--cw:ljc
zxp--pz--ll
# print $1,$2和print $1 $2 是有区别的,主要体现在打印上面
awk 'BEGIN{RS="--";FS=":"}{print $1,$2}' test1.txt | grep -v "^$"
ljc cw
cw ljc
zxp
pz
ll
#
awk 'BEGIN{RS="--";FS=":"}{print $1$2}' test1.txt | grep -v "^$"
ljccw
cwljc
zxp
pz
ll
text3.txt
Hadoop|Spark|Flume--Java|Python|Scala|Go--Allen|Mike|Meggie
⚠️Macos 与 Linux 有不同表现,行与行之间默认会多一个换行符,参照以下例子的第一个区别,后续展示 Macos,Linux 不再展示
{print $1,$3}
如果不用逗号隔开的话,设置 OFS 是没有效果的,会按照默认空格分割
awk 'BEGIN{RS="--"}{print $0}' test3.txt | grep -v "^$"
Hadoop|Spark|Flume
Java|Python|Scala|Go
Allen|Mike|Meggie
awk 'BEGIN{RS="--";FS="|"}{print $3}' test3.txt | grep -v "^$"
Flume
Scala
Meggie
awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $0}' test3.txt | grep -v "^$"
Hadoop|Spark|Flume&&Java|Python|Scala|Go&&Allen|Mike|Meggie& # Macos
awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $0}' test3.txt
Hadoop|Spark|Flume&Java|Python|Scala|Go&Allen|Mike|Meggie # Linux
# {print $1 $3}
awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $1 $3}' test3.txt | grep -v "^$"
HadoopFlume&&JavaScala&&AllenMeggie&
# {print $1,$3}
awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $1,$3}' test3.txt | grep -v "^$"
Hadoop Flume& &Java Scala& &Allen Meggie&
awk 'BEGIN{RS="--";FS="|";ORS="&";OFS="\*"}{print $1,$3}' test3.txt | grep -v "^$"
Hadoop*Flume&*&Java*Scala&*&Allen*Meggie&
# 获取处理文件的文件名
awk '{print FILENAME}' test3.txt
test3.txt
awk '{print FILENAME}' test3.txt test1.txt # 如果没有限定条件,那么输出是按照匹配行的个数来输出的,所以输出可能多次文件名
test3.txt
test1.txt
test1.txt
# 输出命令行参数个数 awk和test3.txt共2个
awk '{print ARGC}' test3.txt
2
# 格式化输出 printf
- ⚠️ 中文有可能使 printf 达不到预期效果
# 语法
{printf "格式符+修饰符", 变量}
# printf 的格式说明符
格式符 | 含义 |
---|---|
%(n)s | 打印字符串 (n 为占位字符,如%20s) |
%d | 打印十进制数 |
%f | 打印一个浮点数(默认 6 位小数) |
%x | 打印十六进制数 |
%o | 打印八进制数 |
%e | 打印数字的科学计数 |
%c | 打印单个字符的 ASCll 码 |
# printf 的修饰符
修饰符 | 含义 |
---|---|
- | 左对齐 |
+ | 右对齐 |
# | 显示八进制在前面加 0,显示十六进制在前面加 0x |
- Linux 和 Macos 在表现上有区别,例如 Linux 下使用修饰符+-必须有位数如
%+15s
printf.txt
ljc:cw:111
cw:ljc:222
cyx:fxl:333
awk '{printf "%s", $0}' printf.txt
ljc:cwcw:ljccyx:fxl
awk '{printf "%s\n", $0}' printf.txt
ljc:cw
cw:ljc
cyx:fxl
awk 'BEGIN{FS=":"}{printf "%s%s\n", $1,$2}' printf.txt
ljccw
cwljc
cyxfxl
# "%s %s\n"变量之间放空格
awk 'BEGIN{FS=":"}{printf "%s %s\n", $1,$2}' printf.txt
ljc cw
cw ljc
cyx fxl
# 使用占位字符(默认右对齐)
awk 'BEGIN{FS=":"}{printf "%20s %20s\n", $1,$2}' printf.txt
ljc cw
cw ljc
cyx fxl
# 使用左右对其方式
awk 'BEGIN{FS=":"}{printf "%-20s %20s\n", $1,$2}' printf.txt
ljc cw
cw ljc
cyx fxl
# 十进制整数
awk 'BEGIN{FS=":"}{printf "%d\n", $3}' printf.txt
111
222
333
# 显示小数
awk 'BEGIN{FS=":"}{printf "%.2f\n", $3}' printf.txt
111.00
222.00
333.00
# 匹配模式
语法格式 | 含义 |
---|---|
RegExp | 按正则表达式匹配 |
运算符 | 按关系运算匹配 |
关系运算符匹配 | 含义 |
---|---|
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
== | 等于 |
!= | 不等于 |
~ | 匹配正则表达式 |
!~ | 不匹配正则表达式 |
布尔运算符匹配 | 含义 |
---|---|
|| | 或 |
&& | 与 |
! | 非 |
pattern.txt
zhang,24,90,85,88,94
wang,22,70,80,75,85
li,31,80,90,60,55
# 匹配含有90的行
awk '/90/{print $0}' pattern.txt
# 匹配第4个元素大于80的并打印出来
awk 'BEGIN{FS=","}$4>80{printf "%-20s %20d\n", $0,$4}' pattern.txt
zhang,24,90,85,88,94 85
li,31,80,90,60,55 90
# 匹配$1正则表达式满足/^zhang/的行
awk 'BEGIN{FS=","}$1~/^zhang/{print $0}' pattern.txt
zhang,24,90,85,88,94
# 匹配$1正则表达式不满足/^zhang/的行
awk 'BEGIN{FS=","}$1!~/^zhang/{print $0}' pattern.txt
wang,22,70,80,75,85
li,31,80,90,60,55
# 匹配$2大于30的或者$1以满足/^wang/的行
awk 'BEGIN{FS=","}$2>30 || $1~/^wang/{print $0}' pattern.txt
wang,22,70,80,75,85
li,31,80,90,60,55
# 表达式
# 动作表达式中的算数运算符
- awk 默认支持浮点数
运算符 | 含义 |
---|---|
+-*/% | 加减乘除余 |
^或** | 乘方 |
++x | 在返回 x 变量之前,x 变量加 1 |
x++ | 在返回 x 变量之后,x 变量加 1 |
--x | 在返回 x 变量之前,x 变量减 1 |
x-- | 在返回 x 变量之后,x 变量减 1 |
awk 'BEGIN{a=1;b=2;print a,b}'
1 2
awk 'BEGIN{a=1;b=2;printf "%.2f", a/b}' | grep -v "^$"
0.50
# 显示空行的个数
grep -wc "^$" test1.txt
# 等价
awk '/^$/{sum++}END{print sum}' pattern.txt
# 动作中的条件及循环语句
# 条件语法
if (条件表达式) {
动作1
} else if(条件表达式) {
动作2
} else {
动作3
}
awk '{if($3>=90) print $0,$3}' student.txt
awk '{if($3>=80 && $3<90) print $0}' student.txt
# 循环语法 while
while (条件表达式){
动作·
}
# 循环语法 do while
do
动作
while (条件表达式)
# 循环语法 for
for (i=0;i<n;i++){
动作
}
# 字符串函数
默认可以使用的关于字符串处理的函数
函数名 | 解释 | 函数返回值 |
---|---|---|
length(str) | 计算字符串长度 | 整数长度值 |
index(str1,str2) | 在 str1 中查询到的 str2 的位置 | 返回值为位置索引,从 1 计数 |
tolower(str) | 小写转换 | 转换后的小写字符串 |
toupper(str) | 大写转换 | 转换后的大写字符串 |
split(str,arr,fs) | 按 fs 分隔字符串,并保存到 arr 中,fs 默认是空格或者 tab | 切割后的字串的个数 |
substr(str,m,n) | 截取子串,从 m 个字符开始,截取 n 位。n 若不指定,则默认截取到字符串尾 | 截取后的字串 |
match(str,RE) | 在 str 中按 /RE/ 查找,返回位置 | 返回索引位置 |
sub(RE,RepStr,str) | 在 str 中按 RE 查找符合的字串并替换成 RepStr,只替换第一个 | 替换的个数 |
gsub(RE,RepStr,str) | 在 str 中按 RE 查找符合的字串并替换成 RepStr,替换所有 | 替换的个数 |
awk-string.txt
root:x:0:0:root:/root:/bin/bash
awk -f string.awk awk-string.txt
BEGIN{
FS=":"
}
{
i=1
while(i<=NF)
{
if(i==NF)
printf "%d",length($i)
else
printf "%d:",length($i)
i++
}
print ""
}
4:1:1:1:4:5:9
搜索字符串"I have a dream"中出现"ea"子串的位置
awk 'BEGIN{str="I hava a dream";location=index(str,"ea");print location}'
awk 'BEGIN{str="I hava a dream";location=match(str,"ea");print location}'
将字符串"Hadoop Kafka Spark Storm HDFS YARN Zookeeper",按照空格为分隔符,分隔每部分保存到数组 array 中
awk 'BEGIN{str="Hadoop Kafka Spark Storm HDFS YARN Zookeeper";split(str,arr);for(a in arr) print arr[a]}'
搜索字符串"Tranction 2345 Start:Select * from master"第一个数字出现的位置
awk 'BEGIN{str="Tranction 2345 Start:Select * from master";location=match(str,/[0-9]/);print location}'
截取字符串"transaction start"的子串,截取条件从第 4 个字符开始,截取 5 位
awk 'BEGIN{str="transaction start";print substr(str,4,5)}'
awk 'BEGIN{str="transaction start";print substr(str,4)}'
替换字符串"Tranction 243 Start,Event ID:9002"中第一个匹配到的数字串为$符号
awk 'BEGIN{str="Tranction 243 Start,Event ID:9002";count=sub(/[0-9]+/,"$",str);print count,str}'
awk 'BEGIN{str="Tranction 243 Start,Event ID:9002";count=gsub(/[0-9]+/,"$",str);print count,str}'
# 数组的用法
数组下标从 1 开始(和 shell 不同)
数组下标不仅可以是数字,也可以是字符串,使用字符串作为下标时候,遍历使用 for (a in array)
使用字符串下标的时候必须用双引号扩起来
BEGIN{
arr["var1"]="word1"
arr["var2"]="word2"
for(a in arr) {
print arr[a]
}
}
# 数组的定义
str="Allen Jerry Mike Tracy Jordan Kobe Garnet"
split(str,array)
# 或
arr["var1"]="word1"
arr["var2"]="word2"
# 遍历语法 1
for (a in array)
print array[a]
awk -f array.awk
BEGIN{
str="Allen Jerry Mike Tracy Jordan Kobe Garnet"
split(str,array)
for(a in array) {
print array[a]
}
}
# 遍历语法 2
for (i=1;i<=length(array);i++)
print array[i]
awk -f array.awk
BEGIN{
str="Allen Jerry Mike Tracy Jordan Kobe Garnet"
split(str,array)
for(i=1;i<=length(array);i++) {
print array[i]
}
}