1.awk概述

定义:AWK 是一种解释型语言,它逐行读取文本输入,根据指定的模式对每一行进行匹配,并执行相应的动作。AWK 程序通常由一系列模式 – 动作对组成。

工作流程:AWK 从输入文件(或标准输入)中读取一行数据,然后将该行数据与程序中定义的各个模式进行匹配。如果某一行匹配某个模式,就执行与该模式关联的动作。动作可以是打印该行的特定字段、进行计算、生成新的输出等。处理完一行后,AWK 读取下一行,重复这个过程,直到输入结束

2.awk 文本处理工具

-F: 指定字段分隔符
-v:进入变量模式 可以进行变量的赋值及调用(调用不需要加$符)
NR:指定获取多少行
$数字:某一列
$NF:最后一列
$(NF - 1) 则表示当前行倒数第二个字段
$1~$n:第几列
$0:表示整个输入行。
-d[file]:启用调试模式。如果指定了file,则将调试信息输出到该文件中;否则输出到标准错误输出。这有助于了解 AWK 脚本的执行过程
-f 选项是 awk 命令中用于指定脚本文件的参数
$n:表示第n个字段。
--posix:使 AWK 遵循 POSIX 标准,确保 AWK 的行为在不同系统上具有一致性,尤其是在处理一些边界情况和特殊功能时
--lint:启用 lint 模式,检查 AWK 脚本中的潜在问题和不规范用法。它会在脚本执行前输出一些警告信息,提示可能存在的问题
&& 并且 同时成立
|| 或者
print 是 awk 中用于输出数据的基本命令
$:用于引用字段。例如,$1 表示第一字段,$2 表示第二字段,$0 表示整个当前记录行。
FS:内置变量,用于设置字段分隔符,默认是空格或制表符。可以通过 -F 选项在命令行设置,也可以在脚本中修改。例如,awk -F, '{print $1}' file.csv 使用逗号作为字段分隔符来打印 file.csv 的第一字段。
RS:内置变量,用于设置记录分隔符,默认是换行符。例如,awk -v RS=, '{print NR,$0}' 把逗号设置为记录分隔符
%s:字符串格式。
%d:十进制整数格式。
%f:浮点数格式
;:语句分隔符,用于在同一行上书写多个语句
[ :]+ 是一个正则表达式,表示匹配一个或多个空格或者冒号。这意味着 awk 将根据一个或多个空格或者冒号来分割输入行的字段
{}:动作块,包含当条件满足时要执行的一系列命令
~ 和 !~:~ 表示匹配正则表达式,!~ 表示不匹配正则表达式。例如,awk '$1 ~ /^[0-9]+$/' file.txt 会打印 file.txt 中第一字段全是数字的行
OFS:指定输出字段分隔符,默认为空格。
^:在正则表达式中,^ 表示行的开头
$:在正则表达式中,$ 表示行的结尾

显示带root的行

awk '/root/' /etc/passwd

显示第10行到第14行的第3列

awk -F: '{if(NR>=10 && NR<=14) print $3 }' /etc/passwd

第一列和第三列

[root@shell ~]$ awk -F: '{print $1,$3}' /etc/passwd

显示第1列和第3列和最后一列使用分隔符

[root@shell ~]$ awk -F: -vOFS=# '{print $1,$3,$NF}' /etc/passwd
awk -F: -vOFS='#####' '{print $1,$3,$NF}'

显示行数

awk 'END { print NR;}' /etc/passwd

查找nginx的进程id

ps aux|grep nginx|grep -v grep|awk '{print $2}'

使用”:”分割,显示文件第1列

awk -F ':' '{print $1}' /etc/passwd

使用”:”分割,显示文件第1,2,3列

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

使用-v设置变量,1+a:在第1列后面添加a列

awk -F ':' -va='666' '{print $1, $1+a}' /etc/passwd

使用-v设置变量,1b:在第1列后面拼接b值作为新的1列

awk -F ':' -va='666' -vb='abc' '{print $1, $1+a, $1b}' /etc/passwd

统计日志出现ip次数

awk '{print $1}' access.log | sort | uniq -c | sort -nr | head

统计日志出现状态码次数

awk '{print $9}' access.log | sort | uniq -c | sort -nr | head

取出top 命令中运行时间

[root@shell ~]$ top -n1 |awk -F'[ ,]+' 'NR==1{print $5}'

统计access.log中 状态码是200的次数,及状态码为200的流量总和

[root@shell ~]$ awk '$9==200{i++ ; sum=sum+$10}END{print "200次数:"i,"200流量总和:"sum}' access.log 
200次数:199 200流量总和:120564

取出网卡配置文件中的ip部分

[root@shell ~]$ awk '/IPADDR/{print }' /etc/sysconfig/network-scripts/ifcfg-eth0

调试参数

awk -d debug.log -f myscript.awk input.txt #调试信息将被记录到debug.log文件中

其他参数

awk --lint -f myscript.awk
awk --posix '{print $0}' input.txt

分别统计每种状态码的次数及流量总和

[root@shell ~]$ awk '
$9~/^2/{ i2++; sum2+=$10 }
$9~/^3/{ i3++; sum3+=$10 }
$9~/^4/{ i4++; sum4+=$10 }
$9~/^5/{ i5++; sum5+=$10 }
END{
print "2xx:",i2,sum2;
print "3xx:",i3,sum3;
print "4xx:",i4,sum4;
print "5xx:",i5,sum5;
}' access.log|column -t

2xx: 199 120564
3xx: 84 0
4xx: 646 315252
5xx:

特殊符号

[root@shell ~]$ cat awk.txt
1 a
2 c
3 d
4 f
5 s
6 z
7 k
8 l
9 m
10 b

匹配第一行

[root@shell ~]$ awk ‘NR==1’ awk.txt
1 a

匹配大于或等于3的行

[root@shell ~]$ awk ‘NR>=3’ awk.txt
3 d
4 f
5 s
6 z
7 k
8 l
9 m
10 b

匹配第3行到第5行

[root@shell ~]$ awk ‘NR>=3 && NR<=5’ awk.txt
3 d
4 f
5 s

!= (不等于)

所有第一列不是 1 的行

[root@shell ~]$ awk ‘$1 != 1’ awk.txt
2 c
3 d
4 f
5 s
6 z
7 k
8 l
9 m
10 b

所有第二列不是 “a” 的行

[root@shell ~]$ awk ‘$2 != “a”‘ awk.txt
2 c
3 d
4 f
5 s
6 z
7 k
8 l
9 m
10 b

第一列数值大于 5 且第二列不是 “s” 的行

[root@shell ~]$ awk ‘NR>5 && $2 != “s”‘ awk.txt
6 z
7 k
8 l
9 m
10 b

第1行和所有从第3行开始到第5行之间(包括第3和第5行),并且第二列不是 “d” 的所有行

[root@shell ~]$ awk ‘NR==1 || (NR>=3 && NR<=5 && $2 != “d”)’ awk.txt
1 a
4 f
5 s

多种使用

[root@shell ~]$ awk ‘NR==1 { print “First line: ” $0 }NR>=3 && NR<=5 && $2 != “d” { print “Line between 3 and 5, second column not 10: ” $0 }’ awk.txt

First line: 1 a
Line between 3 and 5, second column not 10: 4 f
Line between 3 and 5, second column not 10: 5 s

1.NR==1 { print “First line: ” $0 }

模式:NR==1 表示当前行是文件的第一行(记录号 NR 等于1)。
动作:{ print “First line: ” $0 } 当条件为真时,打印整行内容,并在其前面加上 “First line: ” 字符串。
作用:如果当前处理的是文件的第一行,则输出该行的内容,并标注为第一行。

2.NR>=3 && NR<=5 && $2 != “d” { print “Line between 3 and 5, second column not d: ” $0 }
模式:NR>=3 && NR<=5 && $2 != “d” 表示当前行号在3到5之间(包括3和5),并且第二列的值不等于字符串 “d”。
动作:{ print “Line between 3 and 5, second column not d: ” $0 } 当所有条件都满足时,打印整行内容,并在其前面加上 “Line between 3 and 5, second column not d: ” 字符串。
作用:如果当前行在第3到第5行之间,且第二列不是 “d”,则输出该行的内容,并添加描述性前缀。

3.BEGIN

BEGIN 是 awk 中的一个特殊模式,它在处理任何输入之前执行。这意味着,在读取和处理文件内容之前,BEGIN 块中的代码会首先被执行。这个特性使得 BEGIN 非常适合用于初始化变量、设置环境(如字段分隔符)、打印标题或其他预处理任务

BEGIN 的基本概述

定义:BEGIN 是一个关键字,用来标识一个特殊的代码块,该代码块在 awk 开始处理输入数据之前运行。

语法:BEGIN { … }

作用:通常用于初始化操作或设置全局参数,确保这些设置在处理实际数据之前已经完成

设置输入/输出格式

BEGIN {
    FS = ","          # 设置输入字段分隔符为逗号
    OFS = "\t"        # 设置输出字段分隔符为制表符
    ORS = "\n\n"      # 设置输出记录分隔符为双换行
}

使用

[root@shell ~]$ cat cj.txt
Alice,23,85
Bob,22,92
Charlie,24,78
David,21,88

[root@shell ~]$ awk ‘
> BEGIN {
> FS = “,” # 设置输入字段分隔符为逗号
> OFS = “\t” # 设置输出字段分隔符为制表符
> print “Name”, “Age”, “Score” # 打印标题
> }
>
> {
> sum += $3 # 累加分数
> count++ # 计数学生数量
> print $1, $2, $3 # 打印每一行的学生信息
> }
>
> END {
> avg = sum / count # 计算平均分
> print “Average Score:”, avg # 打印平均分
> }’ cj.txt

Name Age Score
Alice 23 85
Bob 22 92
Charlie 24 78
David 21 88
Average Score: 85.75

脚本

[root@shell ~]$ cat awk.sh
awk ‘
BEGIN {
FS = “,” # 设置输入字段分隔符为逗号
OFS = “\t” # 设置输出字段分隔符为制表符
print “Name”, “Age”, “Score” # 打印标题
}

{
sum += $3 # 累加分数
count++ # 计数学生数量
print $1, $2, $3 # 打印每一行的学生信息
}

END {
avg = sum / count # 计算平均分
print “Average Score:”, avg # 打印平均分
}’ cj.txt

[root@shell ~]$ sh awk.sh
Name Age Score
Alice 23 85
Bob 22 92
Charlie 24 78
David 21 88
Average Score: 85.75

注意事项

BEGIN 块仅在 awk 开始处理输入之前执行一次。

如果没有其他模式或动作块,只有 BEGIN 和 END 块存在时,awk 不会读取任何输入文件。

BEGIN 可以与其他模式/动作对结合使用,但它的代码总是最先执行

4.END

END 是 awk 中的一个特殊模式,它在所有输入数据处理完毕后执行。这意味着,在 awk 读取并处理完所有的输入行之后,END 块中的代码会被执行。这使得 END 非常适合用于总结性操作、输出统计信息或清理资源。

END 的基本概述

定义:END 是一个关键字,用来标识一个特殊的代码块,该代码块在 awk 完成所有输入数据的处理后运行。

语法:END { … }

作用:通常用于在处理完所有数据后进行总结、输出统计结果、关闭文件或其他清理工作

[root@shell ~]$ cat cj1.sh
awk ‘
BEGIN {
FS = “,” # 设置输入字段分隔符为逗号
OFS = “\t” # 设置输出字段分隔符为制表符
print “Name”, “Age”, “Score” # 打印标题
}

{
sum += $3 # 累加分数
count++ # 计数学生数量
print $1, $2, $3 # 打印每一行的学生信息
}

END {
if (count > 0) {
avg = sum / count # 计算平均分
print “Average Score:”, avg # 打印平均分
}
}’ cj.txt

[root@shell ~]$ sh cj1.sh
Name Age Score
Alice 23 85
Bob 22 92
Charlie 24 78
David 21 88
Average Score: 85.75

注意事项

END 块仅在 awk 处理完所有输入后执行一次。

如果没有其他模式或动作块,只有 BEGIN 和 END 块存在时,awk 不会读取任何输入文件。

END 可以与其他模式/动作对结合使用,但它的代码总是最后执行。

5.awk数组

awk 中的数组是关联数组(也称为哈希表或字典),这意味着它们不是通过数字索引访问,而是使用键值对的形式。键可以是任意字符串(除了空字符串),而值可以是任何 awk 的变量类型,包括数字、字符串或其他数组(多维数组)。下面是一些关于如何在 awk 中使用数组的基本信息和例子

定义和初始化数组

在 awk 中定义数组不需要声明;你只需直接开始使用它即可。例如:

array["key"] = "value"

这行代码创建了一个名为 array 的数组,并向其中添加了一个键为 “key”、值为 “value” 的元素

使用数组

访问数组元素

你可以通过键来访问数组中的元素:

print array["key"]

如果该键不存在,则返回空字符串

遍历数组

awk 提供了 for 循环来遍历数组的所有元素。需要注意的是,awk 不保证遍历顺序。

for (key in array) {
    print "Key:", key, "Value:", array[key]
}

删除数组元素

你可以使用 delete 语句来删除数组中的某个元素:

delete array["key"]

这将移除与 “key” 相关联的元素。

检查数组中是否存在某键

要检查一个特定的键是否存在于数组中,可以使用 “key” in array 表达式:

if ("key" in array) {
    print "The key exists."
} else {
    print "The key does not exist."
}

多维数组

awk 支持多维数组,但实际上它们是通过将多个键组合成一个复合键来实现的。awk 使用特殊字符 SUBSEP(默认是一个控制字符 \034)来分隔这些键。例如:

two_dim_array["first", "second"] = "value"

要访问这个元素,你可以这样写:

print two_dim_array["first", "second"]

如果你想遍历一个多维数组,你需要记住 SUBSEP 是用来连接键的:

for (key in two_dim_array) {
    split(key, keys, SUBSEP)
    print "Keys:", keys[1], keys[2], "Value:", two_dim_array[key]
}

数组示例

假设我们有一个文件 data.txt,每行包含一个国家名和人口数,用逗号分隔。我们可以使用数组来统计每个国家的人口总数:

# 假设 data.txt 文件内容如下:
# China,1400000000
# India,1366000000
# USA,331000000

BEGIN { FS="," }
{
    country_population[$1] += $2
}
END {
    for (country in country_population) {
        print country, country_population[country]
    }
}

awk 'BEGIN { FS="," } { country_population[$1] += $2 } END { for (country in country_population) print country, country_population[country] }' data.txt | sort

这段脚本会读取 data.txt 文件,计算每个国家的总人口,并在最后打印出结果

统计域名访问次数

[root@shell ~]$ cat url.txt
http://www.etiantian.org/index.html
http://www.etiantian.org/1.html
http://post.etiantian.org/index.html
http://mp3.etiantian.org/index.html
http://www.etiantian.org/3.html
http://post.etiantian.org/2.html

[root@shell ~]$ awk -F'[/.]+’ ‘{url[$2]++}END{for(n in url) print n,url[n]}’ url.txt
www 3
mp3 1
post 2

-F'[/.]+’

-F 选项用于指定字段分隔符。

‘[/.]+’ 是一个正则表达式,表示使用一个或多个斜杠 / 或点号 . 作为字段分隔符。这意味着 URL 中的协议(如 http://)、子域名、顶级域名等都会被分割开来。

{url[$2]++}

$2 表示第二个字段。由于我们使用了 [/.]+ 作为分隔符,对于大多数标准的 URL 格式(例如 http://example.com/path/to/page),$2 将会是主域名(即 example)。

url[$2]++ 创建了一个名为 url 的关联数组,其中键为 $2(即主域名),值为该域名出现的次数。每次遇到相同的域名时,计数器加一。

END {for(n in url) print n, url[n]}

END 块在所有输入行处理完毕后执行。

for(n in url) 遍历 url 数组中的每一个键(即每个唯一的域名)。

print n, url[n] 打印出每个域名及其对应的出现次数。

统计access.log中每个ip使用的流量总和

[root@shell ~]$ awk ‘{h[$1]=h[$1]+$10} END{for(ip in h)print ip,h[ip]}’ access.log |sort -rnk2 |head
192.168.15.87 66102
192.168.15.252 62367
192.168.15.141 59997
192.168.15.235 36108
192.168.15.254 27252
192.168.15.236 26580
192.168.15.72 16707
192.168.15.49 13395
192.168.15.161 11712
192.168.15.105 9834

{h[$1]=h[$1]+$10}

$1 表示每行的第一个字段,通常在 Web 服务器访问日志中是客户端的 IP 地址。

$10 表示每行的第十个字段,这通常对应于 HTTP 响应返回给客户端的数据量(以字节为单位)。注意,不同格式的日志文件可能有不同的字段布局,因此你需要确保 $10 确实是你想要的字段。

h[$1] 是一个关联数组,键是 IP 地址(即 $1),值是该 IP 地址所有记录中 $10 字段的累计和。

h[$1]=h[$1]+$10:对于每一行,如果 h[$1] 已经存在,则将其当前值与 $10 相加;如果不存在,则初始化为 $10。

END{for(ip in h)print ip,h[ip]}

END 块在所有输入行处理完毕后执行。

for(ip in h) 遍历关联数组 h 中的每一个键(即每个唯一的 IP 地址)。

print ip,h[ip] 打印出每个 IP 地址及其对应的累计响应字节数。

统计每个ip出现的次数 及使用的流量

[root@shell ~]$ awk ‘{ cnt[$1]++; sum[$1]+=$10;} END{ for(ip in cnt) print ip,cnt[ip],sum[ip]}’ access.log |column -t |sort -rn -k2 -k3

判断磁盘使用率 大于70% 如果大于 显示磁盘空间不足 如果不大于 显示正常

[root@shell ~]$ df -h |awk -F'[ %]+’ ‘NR>1{if($5>70) print “磁盘空间不足”; else print “正常” };’
正常
正常

[root@shell ~]$ df -h |awk -F'[ %]+’ ‘NR>1 { percent = gensub(/%/, “”, “g”, $5); if( percent +0 > 70 ) { print “不正常(“$6″)” } else { print “正常(“$6″)” ; } }’
正常(/dev)
正常(/dev/shm)
正常(/run)
正常(/sys/fs/cgroup)
正常(/)
正常(/boot)
正常(/run/user/0)

gensub(/%/, “”, “g”, $5):这个函数用于全局替换 $5 中的所有 % 符号为空字符串,从而得到纯数字字符串。

percent + 0:这一步是为了确保即使 percent 是空字符串(理论上不应该发生),也能安全地转换为 0,避免潜在的错误。

$6:这里假设你想输出的是文件系统名,即第六列。如果你想要显示不同的信息,请根据实际需求调整。

NR > 1:跳过 df -h 输出的第一行(标题行)。

输出格式化:我添加了 ($6) 来指出哪个设备空间不足或正常,这样可以更清楚地知道具体是哪个磁盘有问题。

段分隔符 -F'[[:space:]]+|%+’:

使用 [[:space:]]+ 来匹配一个或多个空白字符(包括空格和制表符),这比单独使用空格更灵活。

使用 %+ 来匹配一个或多个 % 符号,以处理可能存在的额外百分号。

直接对 $5 进行 + 0 操作来移除百分比符号并转换为数值类型。因为 df -h 的输出格式相对固定,这样做通常足够可靠

作者 dmxsp

发表回复

您的电子邮箱地址不会被公开。