一、概念
awk
是一种强大的文本处理工具,用于在文本文件中执行各种文本处理任务。它的基本语法如下:
awk 'pattern { action }' input_file
-
pattern
:用于匹配输入行的模式。如果省略模式部分,awk
将对所有输入行执行指定的动作。 -
{ action }
:在模式匹配的情况下执行的操作。操作可以是一系列的命令,用花括号括起来。通常,这些操作用于对匹配模式的行执行某些操作,如打印、计算等。 -
input_file
:要处理的输入文件的名称。如果省略了输入文件,则awk
将从标准输入中读取数据。
awk
的工作步骤:
-
读取(Read):
awk
从输入源(可以是文件、管道或标准输入)逐行读取数据。每一行数据都会被分割成字段,字段之间的默认分隔符是空格或制表符。这些行和字段会被存储在内存中,以供后续处理。 -
执行(Execute):
awk
对每一行的数据执行一系列的操作,这些操作通常在模式匹配成功时执行。操作可以是打印、计算、字符串处理、条件判断、循环等。awk
默认会处理每一行数据,但也可以根据指定的模式来选择性地处理特定行。 -
重复(Repeat):
awk
会持续重复执行读取和执行操作的过程,直到输入源中的所有行都被处理完毕。
awk
还有一些选项和内置变量,可用于更精确地控制其行为。以下是一些常用的awk
选项和内置变量:
选项:
-
-F
:用于指定输入字段分隔符。默认情况下,字段分隔符是空格或制表符。 -
-v var=value
:用于定义awk
变量并赋值。 -
-f scriptfile
:从指定的脚本文件中读取awk
程序。 -
-W option
:用于启用gawk
扩展功能。
内置变量:
-
NF
:表示当前行的字段数量。 -
NR
:表示当前行的记录号(行号)。 -
$0
:表示整个当前行的内容。 -
$1
、$2
、...、$NF
:分别表示当前行的第1、第2、...、最后一个字段的内容。 -
FS
:表示字段分隔符,可以使用-F
选项来设置,或者在BEGIN
块中赋值。 -
OFS
:表示输出字段分隔符,用于控制输出行中字段之间的分隔符,默认为空格。 -
RS
:表示记录分隔符,用于指定输入数据中记录之间的分隔符,默认为换行符。 -
ORS
:表示输出记录分隔符,用于控制输出行之间的分隔符,默认为换行符。 -
FILENAME
:表示当前处理的文件的名称。 -
ARGC
:表示命令行参数的数量(包括命令本身)。 -
ARGV
:一个数组,包含命令行参数的值
以下是一些awk
的示例用法:
-
打印文件中的所有行:
awk '{ print }' input_file
-
使用字段分隔符
-F
以逗号分隔字段并打印第2个字段:awk -F ',' '{ print $2 }' input_file
-
计算文件中数字的总和:
awk '{ sum += $1 } END { print sum }' input_file
-
在匹配模式的行上执行操作:
awk '/pattern/ { print $2 }' input_file
二、模式
awk
的模式可以有多种格式,包括以下几种常见的:
-
正则表达式模式: 正则表达式模式用斜杠
/
包围,用于匹配输入行中包含某种模式的文本。例如,/pattern/
可以匹配包含pattern
的行。 -
比较模式: 比较模式用于比较字段的值。比较模式的一般格式是
expression comparison value
,其中expression
是一个字段、内置变量或表达式,comparison
是比较操作符,value
是与expression
进行比较的值。比较操作符可以是==
(等于)、!=
(不等于)、>
(大于)、<
(小于)、>=
(大于或等于)、<=
(小于或等于)等。例如,
$3 > 10
可以匹配第3个字段的值大于10的行。 -
范围模式: 范围模式用于指定一个范围,在这个范围内执行操作。它的一般格式是
/start_pattern/, /end_pattern/
,其中start_pattern
和end_pattern
是正则表达式模式,用于指定范围的开始和结束。例如,
/start/, /end/ { action }
可以在匹配从包含 "start" 到包含 "end" 的行之间的行时执行操作。 -
逻辑组合模式: 可以使用逻辑操作符(
&&
、||
、!
)将多个模式组合成复杂的模式。例如,/pattern1/ && /pattern2/
表示只有当同时匹配pattern1
和pattern2
时才执行操作。
下面是一些示例:
-
匹配包含 "error" 或 "fail" 的行:
awk '/error/ || /fail/ { action }' input_file
-
匹配第2列等于 "example" 且第3列大于等于 10 的行:
awk '$2 == "example" && $3 >= 10 { action }' input_file
-
匹配从包含 "start" 到包含 "end" 的行并执行操作:
awk '/start/, /end/ { action }' input_file
三、动作
awk
中的动作是在模式匹配成功时执行的一系列命令或操作,用于处理匹配的输入行。动作通常由花括号 { }
括起来,可以包括一个或多个命令,每个命令占据一行。以下是一些常见的 awk
动作和命令:
-
打印输出(Print): 用于打印匹配行或处理结果到标准输出。
-
print
:打印整个输入行,例如print
或print $0
。 -
print expression
:打印一个表达式的值,例如print $1
打印第一个字段的内容。 -
printf
:使用格式化字符串打印输出,例如printf "Name: %s, Age: %d\n", $1, $2
。
-
-
赋值操作: 用于将值赋给变量。
-
variable = value
:将值赋给一个变量,例如sum = sum + $1
可以用于累加字段值。
-
-
条件语句(if-else): 用于在匹配行上执行条件性操作。
if (condition) { # 条件满足时执行的命令 } else { # 条件不满足时执行的命令 }
-
循环语句: 用于在匹配行上执行循环操作。
-
for (initialization; condition; increment) { commands }
:for
循环。 -
while (condition) { commands }
:while
循环。 -
do { commands } while (condition)
:do-while
循环。
-
-
删除行: 用于删除匹配的行。
-
delete
:删除当前行,例如/pattern/ { delete }
。
-
-
内置函数: 你可以使用内置函数执行各种操作,如字符串处理、数学运算等。例如,
length()
函数用于计算字符串长度,gsub()
用于全局替换字符串等。 -
内置变量: 使用内置变量来访问有关输入行和记录的信息,如
$0
(整行内容)、$1
(第一个字段)、NF
(字段数)、NR
(记录号)等。 -
特殊命令: 一些特殊的
awk
命令用于控制流程,如next
(跳到下一行)、break
(退出循环)、exit
(退出awk
)等。
这些是 awk
中常见的动作和命令,你可以根据具体的文本处理需求组合它们以完成任务。 awk
的强大之处在于它允许你编写复杂的脚本来处理文本数据。
四、BEGIN和END模块
-
BEGIN 模块:
BEGIN
模块是在awk
开始处理输入之前执行的一段代码块。通常用于初始化变量、打印标题或执行其他预处理任务。BEGIN模块在AWK脚本的开头定义,如下所示:BEGIN { # 这里是BEGIN模块的代码 }
例如,你可以在BEGIN模块中打印标题行,然后在处理数据之前显示:
BEGIN { print "Name\tAge" print "-------------------" } { # 在这里处理数据 }
-
END 模块:
END
模块是在awk
处理输入后执行的一段代码块。通常用于总结处理后的结果,输出总计或执行其他收尾任务。END模块也在AWK脚本的末尾定义,如下所示:END { # 这里是END模块的代码 }
例如,你可以在END模块中输出总计,如下所示:
{ # 在这里处理数据 } END { print "-------------------" print "Total: " total }
BEGIN和END模块不必同时出现在AWK脚本中,你可以根据需要选择使用其中之一或两者都使用。这些模块通常用于在处理文本之前进行初始化和在处理文本之后执行总结或清理任务。
五、示例
1、打印出passwd
文件中用户ID小于10的用户名和它登录使用的SHELL
[root@fishman-160 ~]# awk -F: '$3<10 {printf "%-20s %-20s\n",$1,$NF}' /etc/passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
shutdown /sbin/shutdown
halt /sbin/halt
mail /sbin/nologin
2、打印出系统中能够正常登录的普通用户
[root@fishman-160 ~]# awk -F: '$3>1000&&$NF=="/bin/bash" {printf "%-20s %-20s\n",$1,$NF}' /etc/passwd
sp1 /bin/bash
sp2 /bin/bash
sp3 /bin/bash
#/bin/bash需要双引号
3、添加开始和结束模块
awk
脚本
1 BEGIN{
2 printf "%-20s %-20s\n","Userid","Shell"
3 print "------------------------"
4 FS=":"
5 }
6 $3>=500 && $7=="/sbin/nologin"{
7 printf "%-20s %-20s\n",$1,$NF
8 }
9 END{
10 print "------------------------"
11 }
执行
[root@fishman-160 ~]# awk -f test.awk /etc/passwd
Userid Shell
------------------------
nobody /sbin/nologin
systemd-coredump /sbin/nologin
polkitd /sbin/nologin
geoclue /sbin/nologin
unbound /sbin/nologin
pipewire /sbin/nologin
clevis /sbin/nologin
gluster /sbin/nologin
chrony /sbin/nologin
dnsmasq /sbin/nologin
saslauth /sbin/nologin
libstoragemgmt /sbin/nologin
sssd /sbin/nologin
cockpit-ws /sbin/nologin
cockpit-wsinstance /sbin/nologin
colord /sbin/nologin
setroubleshoot /sbin/nologin
flatpak /sbin/nologin
gnome-initial-setup /sbin/nologin
pesign /sbin/nologin
4、统计当前内存的使用率
[root@fishman-160 ~]# free -m | grep Mem |awk '{printf "%.2f%\n",$3/$2*100}'
37.29%
[root@fishman-160 ~]# free -m | awk '/Mem/ {printf "%.2f%\n",$3/$2*100}'
37.29%
awk
的高级应用
一、if...else...
在 awk
中,可以使用条件判断来根据某些条件来执行不同的操作。条件判断通常与模式匹配结合使用,以确定是否对某行数据执行操作。以下是 AWK 中条件判断的基本语法:
pattern {
# 条件判断语句
if (condition) {
# 在条件满足时执行的代码
} else {
# 在条件不满足时执行的代码
}
}
其中:
-
pattern
是一个可选的模式,用于确定哪些行将受到条件判断的影响。如果省略模式,条件判断将应用于所有输入行。 -
condition
是一个用于评估真假的表达式。如果条件为真,则执行if
子句中的代码块;否则,执行else
子句中的代码块。
以下是一个示例,演示了如何在awk
中使用条件判断:
awk '{
if ($3 >= 500) {
printf "%-20s%s\n", $1, " has a UID greater than or equal to 500"
} else {
printf "%-20s%s\n", $1, " has a UID less than 500"
}
}' FS=":" /etc/passwd
在这个示例中,我们检查 /etc/passwd
文件中每行的第3个字段(UID
),如果UID
大于等于500,则打印一条消息,否则打印另一条消息。
示例:如果passwd
中的uid
小于10,则变量USER赋值成aaa
,否则赋值成bbb
[root@fishman-160 ~]# awk -F: '{if ($3<10) USER="aaa";else USER="bbb"; print $1,USER}' /etc/passwd
root aaa
bin aaa
daemon aaa
adm aaa
lp aaa
sync aaa
shutdown aaa
halt aaa
mail aaa
operator bbb
games bbb
ftp bbb
nobody bbb
dbus bbb
systemd-coredump bbb
...
[root@fishman-160 ~]# awk -F: '{$3<10? USER="aaa":USER="bbb";print $1,USER}' /etc/passwd
root aaa
bin aaa
daemon aaa
adm aaa
lp aaa
sync aaa
shutdown aaa
halt aaa
mail aaa
operator bbb
games bbb
ftp bbb
..
二、while
循环
while
循环用于在给定条件为真的情况下反复执行一段代码块。语法如下:
while (condition) {
# 循环体
}
-
condition
是一个用于评估真假的表达式。 -
循环体是你希望重复执行的代码块。
以下是一个示例,演示了如何在 AWK 中使用 while
循环:
假设你的 input.txt
文件包含以下内容:
This is a sample line.
Another line of text.
One more line here.
脚本如下
awk '{
i = 1
while (i <= 5) {
print "Line " i ": " $0
i++
}
}' input.txt
这个示例中,while
循环会将当前行打印五次,每次行号递增。
三、for
循环
for
循环用于在给定条件为真的情况下按指定的步长重复执行一段代码块。语法如下:
for (init; condition; increment) {
# 循环体
}
-
init
是初始化循环变量的表达式。 -
condition
是循环条件,当条件为真时循环继续执行。 -
increment
是循环迭代的表达式,通常用于增加或减少循环变量的值。
以下是一个示例,演示了如何在 AWK 中使用 for
循环:
awk '{
for (i = 1; i <= 5; i++) {
print "Line " i ": " $0
}
}' input.txt
这个示例中,for
循环会将当前行打印五次,每次行号递增。
四、引用变量
1.外部变量
在awk
中,要引用外部变量,你可以通过使用 -v
选项将外部变量传递给 awk
脚本。
这样,在 awk
脚本中就可以使用传递的外部变量。
下面是如何进行操作的步骤:
-
使用
-v
选项传递外部变量给awk
。语法如下:awk -v variable_name=value 'AWK_SCRIPT' input_file
-
variable_name
是你想要在awk
中使用的变量的名称。 -
value
是你想要传递给变量的值。 -
AWK_SCRIPT
是你的awk
脚本。
-
-
在
awk
脚本中,你可以使用传递的外部变量variable_name
。例如:awk -v my_var=42 '{print "External variable value: " my_var, "Field value: " $1}' input_file
在这个示例中,my_var
是外部变量,它的值是42。在 awk
脚本中,我们使用 my_var
打印了外部变量的值以及输入行的第一个字段的值。
2.内部变量
在 awk
中,引用内部变量(在 awk
脚本中定义的变量)非常简单,你只需使用变量名即可。以下是一个示例:
awk 'BEGIN {
var = "test" # 定义内部变量 var 并赋值为 "test"
print var # 引用并打印内部变量 var
}'
在这个示例中,var
是一个内部变量,它在 BEGIN
块中被定义和赋值为 "test",然后被引用并打印。所以,这将打印 "test"。
你可以在awk
脚本的任何地方定义和引用内部变量,只需使用合适的变量名和赋值即可。这与外部变量(使用 -v
选项传递的变量)不同,外部变量需要额外的参数来传递。
五、格式化输出
awk
提供了强大的格式化输出功能,允许你以特定的格式打印和显示数据。你可以使用 printf
函数来实现格式化输出。下面是使用 printf
函数的基本语法:
printf "格式字符串", 表达式1, 表达式2, ...
-
格式字符串
包含了要打印的文本和格式化指令,例如%s
、%d
、%f
等,这些指令用于指定输出的格式。 -
表达式1
、表达式2
等是要在格式字符串中替换的值。你可以使用多个表达式,每个表达式对应一个格式化指令。
以下是一些常见的格式化指令和示例:
-
%s
:字符串。 -
%d
:十进制整数。 -
%f
:浮点数。 -
%c
:字符。
示例:
awk 'BEGIN {
name = "John"
age = 30
salary = 50000.50
printf "Name: %s\n", name
printf "Age: %d\n", age
printf "Salary: %.2f\n", salary
}'
这个awk
脚本将以格式化的方式打印出姓名、年龄和工资,分别使用 %s
、%d
和 %.2f
进行格式化。输出如下:
Name: John
Age: 30
Salary: 50000.50
示例:格式化输出系统用户名和登录后使用的shell
[root@fishman-160 ~]# awk -F: '{printf "%-10s%-20s%-20s\n","USERNAME:",$1,$NF}' /etc/passwd
示例:格式化输出如下信息
xm 11 12 13
xmm 31 32 33
xh 22 23 24
[root@fishman-160 test]# awk '{printf "%-3s %2d %2d %2d\n",$1,$2,$3,$4}' testfile1.txt
xm 11 12 13
xmm 31 32 33
xh 22 23 24
六、正则表达式
在 awk
中,正则表达式是一种用于匹配文本模式的强大工具。正则表达式通常用于搜索、替换和过滤文本数据。awk
支持正则表达式的使用,你可以在模式中使用正则表达式来匹配文本。
1、 awk
中正则符号和运算符
符号/运算符 | 描述 | 示例 | 匹配的示例 |
---|---|---|---|
. | 匹配任意字符,除了换行符。 | a.b |
aAb 、a1b 、a@b |
* | 匹配前一个字符的零次或多次出现。 | ab*c |
ac 、abc 、abbc |
+ | 匹配前一个字符的一次或多次出现。 | ab+c |
abc 、abbc |
? | 匹配前一个字符的零次或一次出现。 | ab?c |
ac 、abc |
| | 逻辑或,匹配多个模式中的任何一个。 | pattern1|pattern2 |
pattern1 或 pattern2 |
[ ] | 字符类,匹配字符集中的任何一个字符。 | [abc] |
"a"、"b"、"c" |
\ | 转义特殊字符,匹配它们本身。 | \\. |
"." |
( ) | 分组子表达式,用于复杂的模式匹配。 | (abc)\* |
"abc"、"abcabc" |
^ | 匹配行的开头。 | ^start |
"start of line"、"starting point" |
$ | 匹配行的结尾。 | end$ |
"end of line"、"tail end" |
[[:alnum:]] | 匹配字母和数字字符。 | [[:alnum:]] |
"a1", "B2", "123" |
[[:alpha:]] | 匹配字母字符。 | [[:alpha:]] |
"abc", "XYZ" |
[[:digit:]] | 匹配数字字符。 | [[:digit:]] |
"123", "0", "9" |
[[:space:]] | 匹配空白字符(空格、制表符等)。 | [[:space:]] |
" ", " \t", "\n" |
2、示例
-
匹配包含特定单词的行:
awk '/pattern/ {print}' filename
这将打印包含 "pattern" 的行。
-
匹配以特定单词开头的行:
awk '/^pattern/ {print}' filename
这将打印以 "pattern" 开头的行。
-
匹配以特定单词结尾的行:
awk '/pattern$/ {print}' filename
这将打印以 "pattern" 结尾的行。
-
匹配一个字符范围:
awk '/[0-9]/ {print}' filename
这将打印包含数字的行。
-
使用通配符
.
匹配任何字符:awk '/a.b/ {print}' filename
这将匹配
a
,然后是任何字符,然后是b
的行,例如aab
、a1b
等。 -
使用
\*
匹配前一个字符的零次或多次出现:awk '/ab*c/ {print}' filename
这将匹配
ac
、abc
、abbc
等。 -
使用
+
匹配前一个字符的一次或多次出现:awk '/ab+c/ {print}' filename
这将匹配
abc
、abbc
等,但不匹配ac
。 -
使用
?
匹配前一个字符的零次或一次出现:awk '/ab?c/ {print}' filename
这将匹配
ac
和abc
,但不匹配abbc
。
-
对如下数据做格式化输出并计算总成绩
xm 11 12 13 xmm 31 32 33 xh 22 23 24
awk 'BEGIN{printf "%-2s %-2s %-2s %-2s %-2s\n","姓名","语文","英语","数学","总 分"} {printf "%-4s %-4d %-4d %-4d %-4d\n",$1,$2,$3,$4,($2 + $3 + $4); sum+=$2+$3+$4; sum_chinese+=$2; sum_english+=$3; sum_math+=$4} END{printf "%-2s %-4d %-4d %-4d %-4d\n","总计", sum_chinese, sum_english, sum_math, sum}' testfile1.txt
-
通过编写脚本的方式来实现总成绩统计
查看testfile1.txt
内容
[root@fishman-160 test]# cat testfile1.txt
xm 11 12 13
xmm 31 32 33
xh 22 23 24
开始写脚本awk.sh
[root@fishman-160 test]# vim awk.sh
脚本内容明细
BEGIN{
print "-------start awk--------"
printf "%2s %2s %2s %2s %3s\n","姓名","语文","数学","英语", "总成绩"
}
{
total=0
i=2
while(i<=NF){
total+=$i
i++
}
printf "%-4s %-4d %-4d %-4d %-4d\n",$1,$2,$3,$4,total
if(total>max){
max=total
}
}
END{
printf "最高分为:%-4d\n",max
}
awk
导入脚本
[root@fishman-160 test]# awk -f awk.sh testfile1.txt
-------start awk--------
姓名 语文 数学 英语 总成绩
xm 11 12 13 36
xmm 31 32 33 96
xh 22 23 24 69
最高分为:96
七、内置函数
常用的 awk
内置函数:
-
字符串函数:
-
length(str)
: 返回字符串的长度。 -
index(str, search)
: 返回字符串中第一次出现搜索字符串的位置。 -
substr(str, start [, length])
: 返回字符串的子串,从start
开始,可选地指定长度。
-
-
数值函数:
-
int(expr)
: 将表达式转换为整数。 -
sqrt(expr)
: 返回表达式的平方根。 -
log(expr)
: 返回表达式的自然对数。 -
sin(expr)
,cos(expr)
,atan2(y, x)
: 分别是正弦、余弦和反正切函数。
-
-
日期和时间函数:
-
systime()
: 返回当前时间的时间戳(自1970年1月1日以来的秒数)。 -
strftime(format [, timestamp])
: 根据格式字符串返回格式化的时间字符串。默认为当前时间,可选地接受时间戳参数。
-
-
数组函数:
-
length(array)
: 返回数组的元素数量。 -
delete array[index]
: 删除数组的指定元素。
-
-
其他函数:
-
split(str, array [, fieldsep])
: 将字符串分割为数组。 -
gsub(regexp, replacement, str)
: 全局替换字符串中的匹配项。gsub
是 Awk 编程语言中的一个内置函数,用于全局替换字符串中的指定模式。其语法如下:gsub(regexp, replacement, target)
-
regexp
:要查找和替换的正则表达式模式。 -
replacement
:用于替换匹配项的字符串。 -
target
:要进行替换操作的目标字符串。
gsub
在目标字符串中查找所有与正则表达式匹配的部分,并用指定的替换字符串替换它们。如果目标字符串中有多个匹配项,所有匹配项都将被替换。以下是一个简单的例子,说明了
gsub
的基本用法:echo "Hello, World! World!" | awk '{gsub(/World/, "Universe"); print $0}'
在这个例子中,字符串 "Hello, World! World!" 中的所有 "World" 都被替换为 "Universe"。输出将是 "Hello, Universe! Universe!"。
-
-
sprintf(format, expr1, expr2, ...)
: 根据格式字符串生成格式化的字符串。
-
八、补充各类操作符
-
比较操作符:
-
==
:等于 -
!=
:不等于 -
<
:小于 -
>
:大于 -
<=
:小于或等于 -
>=
:大于或等于
-
-
算术操作符:
-
+
:加 -
-
:减 -
*
:乘 -
/
:除 -
%
:取余(取模)
-
-
逻辑操作符:
-
&&
:逻辑与 -
||
:逻辑或 -
!
:逻辑非
-
-
字符串操作符:
-
=
:字符串赋值 -
==
:字符串等于 -
!=
:字符串不等于 -
~
:正则表达式匹配 -
!~
:正则表达式不匹配
-
-
其他操作符:
-
++
:自增 -
--
:自减 -
+i
和-i
:对复数进行加减运算(仅在支持复数的系统上可用)
-
-
特殊操作符:
-
|
:管道,将前一个命令的输出传递给后一个命令。 -
$0
:代表当前行。 -
$1
,$2
, ...:代表当前行的第1、2、...个字段。 -
NF
:代表当前行的字段数量。 -
NR
:代表当前处理的行号。
-