awk
介绍
awk
是一个文本处理工具,通常用于从文本文件中提取数据并对其进行处理。它是一种解释型语言,可以从标准输入或文件中读取数据,并对这些数据进行逐行处理。
语法
awk options 'pattern { action }' filename
其中,options为可选参数。
pattern是一个正则表达式,用于匹配输入文件中的行。如果省略pattern,则默认匹配所有行。
action是一个或多个命令,用于在匹配到的行上执行操作。如果省略action,则默认打印匹配到的行。
filename是要处理的文件的名称。如果省略filename,则默认从标准输入读取数据。
awk 'BEGIN{commands}pattern{commands}END{commands}' file
BEGIN{commands}
:处理数据前执行的命令pattern{commands}
:每行都执行的命令END{commands}
:处理数据后执行的命令
awk的命令执行过程
- 执行
BEGIN{commands}
语句块中的语句 - 从文件或stdin中读取第一行
- 有无模块匹配,若无执行{}中的语句,
- 若有则检查该行与patter是否匹配,若匹配,则执行{}中的语句。
- 若不匹配则不执行{}中的语句,接着读取下一行
- 重复这个过程,直到所有行被读取完毕
- 最后执行
END{commands}
语句块中的语句。
注意事项
awk -F分隔符 '/模式/{动作}' 输入文件
awk的指定一定要用单引号括起
awk的动作一定要用花括号括起
模式可以是正则表达式,条件表达式或两种组合
如果模块是正则表达式要用/定界符
多个动作之间用;
号分开
例如:
在去匹配grade.txt文件之间先输出"你好",定义变量i的值为0,随后开始去匹配grade.txt文件每行的第一个字段是否含有li
,如果含有li
,输出该字段,同时变量i+1,在匹配完grade.txt文件的所有行后,输出"结束了",同时输出含有li
的行数。
awk -F":" 'BEGIN{print "你好";i=0} $1 ~/li/{print
$1;i+=1}END{print "结束了" i}' grade.txt
不显示以h
开头的行的第一列和第七列
[root@localhost shell-test]# awk -F: '/^[^h]/{print $1,$7}' /etc/p
选项
- -F:指定输入字段分隔符。例如,-F:表示使用冒号作为字段分隔符。
- -v:定义awk变量,可以在awk程序中使用。例如,-v var=value定义变量var,并设置其值为value。
- -f:指定awk程序文件。例如,-f script.awk指定使用脚本文件script.awk作为awk程序。
- -W:设置awk的行为。例如,-W posix启用POSIX兼容模式,-W traditional启用传统模式。
- -S:启用seccomp沙箱模式,增强awk的安全性。
基本用法
先创建log.txt文本
[root@localhost 4-18]# vim log.txt
[root@localhost 4-18]# cat log.txt
2 this is a test
3 Do you like awk
This's a test
10 There are orange,apple,mongo
用法一
awk '{[pattern] action}' {filenames} # 行匹配语句 awk '' 只能用单引号
实例
# 每行按空格或TAB分割,输出文本中的1、4项
$ awk '{print $1,$4}' log.txt
---------------------------------------------
2 a
3 like
This's
10 orange,apple,mongo
# 格式化输出
$ awk '{printf "%-8s %-10s\n",$1,$4}' log.txt
---------------------------------------------
2 a
3 like
This's
10 orange,apple,mongo
用法二:
awk -F #-F相当于内置变量FS, 指定分割字符
实例:
# 使用","分割
$ awk -F, '{print $1,$2}' log.txt
---------------------------------------------
2 this is a test
3 Do you like awk
This's a test
10 There are orange apple
# 或者使用内建变量
$ awk 'BEGIN{FS=","} {print $1,$2}' log.txt
---------------------------------------------
2 this is a test
3 Do you like awk
This's a test
10 There are orange apple
# 使用多个分隔符.先使用空格分割,然后对分割结果再使用","分割
$ awk -F '[ ,]' '{print $1,$2,$5}' log.txt
---------------------------------------------
2 this test
3 Are awk
This's a
10 There apple
用法三:
awk -v # 设置变量
实例:
$ awk -va=1 '{print $1,$1+a}' log.txt
---------------------------------------------
2 3
3 4
This's 1
10 11
$ awk -va=1 -vb=s '{print $1,$1+a,$1b}' log.txt
---------------------------------------------
2 3 2s
3 4 3s
This's 1 This'ss
10 11 10s
用法四:
awk -f {awk脚本} {文件名}
实例:
$ awk -f cal.awk log.txt
运算符
运算符 | 描述 |
---|---|
= += -= *= /= %= ^= **= | 赋值 |
?: | C条件表达式 |
|| | 逻辑或 |
&& | 逻辑与 |
~ 和 !~ | 匹配正则表达式和不匹配正则表达式 |
< <= > >= != == ~ ! | 关系运算符 |
空格 | 连接 |
+ - | 加,减 |
* / % | 乘,除与求余 |
+ - ! | 一元加,减和逻辑非 |
^ *** | 求幂 |
++ – | 增加或减少,作为前缀或后缀 |
$ | 字段引用 |
in | 数组成员 |
过滤第一列大于2的行
$ awk '$1>2' log.txt #命令
#输出
3 Do you like awk
This's a test
10 There are orange,apple,mongo
过滤第一列等于2的行
$ awk '$1==2 {print $1,$3}' log.txt #命令
#输出
2 is
过滤第一列大于2并且第二列等于’Are’的行
$ awk '$1>2 && $2=="Are" {print $1,$2,$3}' log.txt #命令
#输出
3 Are you
注意事项
&&
比||
的优先级要高,||
比!
要高
文本数据表达式: ==(精确匹配)
~波浪号表示匹配后的模式(模糊匹配)
例子:
有grade.txt文本如下:
[root@localhost shell-test]# cat grade.txt
name chinese math english
xiaoming 80 90 99
lihua 89 70 99
yang 88 80 90
yangle 80 97 89
精确匹配每行第一个字段是yang
的行
[root@localhost shell-test]# awk '$1 == "yang"{print $0}' grade.txt
----------------------------------------------------
yang 88 80 90
模糊匹配每行第一个字段是yang
的行
[root@localhost shell-test]# awk '$1 ~/yang/{print $0}' grade.txt
------------------------------------------------
yang 88 80 90
yangle 80 97 89
内建变量
变量 | 描述 |
---|---|
$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) |
使用
使用正则,字符串匹配
# 输出第二列包含 "th",并打印第二列与第四列
$ awk '$2 ~ /th/ {print $2,$4}' log.txt
---------------------------------------------
this a
- 表示模式开始。// 中是模式。
# 输出包含 "re" 的行
$ awk '/re/ ' log.txt
---------------------------------------------
3 Do you like awk
10 There are orange,apple,mongo
- 忽略大小写
$ awk 'BEGIN{IGNORECASE=1} /this/' log.txt
---------------------------------------------
2 this is a test
This's a test
- 模式取反
$ awk '$2 !~ /th/ {print $2,$4}' log.txt
---------------------------------------------
Are like
a
There orange,apple,mongo
$ awk '!/th/ {print $2,$4}' log.txt
---------------------------------------------
Are like
a
There orange,apple,mongo
- 打印passwd文件中的第一列数据,以
:
为分割符
[root@localhost shell-test]# awk -F":" '{print $1}' /etc/passwd
-
显示每行的内容和行号
[root@localhost shell-test]# awk -F: 'BEGIN{OFS="@"}/bash$/{print NR,$1,$7}' /etc/passwd 1@root@/bin/bash 21@bailongma@/bin/bash
在 /etc/passwd 文件中查找包含 “bash” 字符串的行,并将它们的行号、用户名和默认 shell 以指定的格式输出
也可以和管道(|)连用,效果一样
[root@localhost shell-test]# cat /etc/passwd|awk -F":" '{print $1}'
打印最后一行
[root@localhost shell-test]# awk -F":" '{print $NF}' /etc/passwd
#打印最后一行
$1中过滤出含sc的第一列
[root@localhost shell-test]# awk -F":" '$1~/sc/{print $1}' /etc/passwd
-
打印出grade.txt文件中的第三列数的和
[root@localhost shell-test]# cat grade.txt name chinese math english xiaoming 80 90 99 lihua 89 70 99 yang 88 80 90 ------------------------------------------------- [root@localhost shell-test]# awk 'NR>1{sum += $3}END{print sum}' grade.txt 240
还可以再求和前输出提示信息:
[root@localhost shell-test]# awk 'BEGIN{print "数学总分数"}NR>1{sum += $3}END{print sum}' grade.txt 数学总分数 240
打印数学成绩在80以上的学生姓名
[root@localhost shell-test]# awk 'BEGIN{print "数学总分数"}NR>1{sum += $3;if( $3>=80 ) print $1 }END{print sum}' grade.txt 数学总分数 xiaoming yang 240
-
打印出passwd用户名长度超过10的字段。
[root@localhost shell-test]# awk -F: 'length($1)>10{print $0}' /etc/passwd systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
内置函数
在Shell中,awk
是一种强大的文本处理工具,它支持使用函数对输入文本进行处理和转换。awk
内置了一些常用的函数,同时也可以自定义函数来满足特定需求。下面是对awk
函数的详细解释。
awk
提供了一些内置函数,这些函数可以直接在awk
程序中使用,无需额外定义。
一些常用的内置函数包括:
length(str)
:返回字符串str
的长度。substr(str, start, length)
:从字符串str
中提取起始位置为start
,长度为length
的子字符串。index(str, target)
:返回字符串str
中第一次出现目标字符串target
的位置。split(str, array, sep)
:将字符串str
按照分隔符sep
分割成多个子字符串,并将结果存储在数组array
中。tolower(str)
:将字符串str
中的大写字母转换为小写字母。toupper(str)
:将字符串str
中的小写字母转换为大写字母。sprintf(format, expr1, expr2, ...)
:根据指定的格式字符串format
,将表达式expr1
、expr2
等转换为格式化的字符串。int(expr)
:返回表达式expr
的整数部分。rand()
:生成一个0到1之间的随机数。strftime()
:函数用于将时间戳转换为格式化的时间字符串。systime()
:函数用于获取当前系统的时间戳。system()
:函数用于执行Shell命令并返回命令的退出状态码
这只是一小部分awk
的内置函数,还有其他许多函数可供使用。您可以参考awk
的文档或手册以获取完整的内置函数列表。
自定义函数
除了内置函数,awk
还支持定义自定义函数,以便在awk
程序中使用。
自定义函数的语法如下:
function 函数名(参数列表) {
函数体
return 返回值
}
其中,函数名
是自定义函数的名称,参数列表
是函数接受的参数,函数体
是函数的执行逻辑,返回值
是可选的返回结果。
以下是一个示例,展示如何定义和使用自定义函数:
# 定义自定义函数
function multiply(x, y) {
return x * y
}
# 使用自定义函数
{
result = multiply($1, $2)
print "结果:", result
}
上述示例定义了一个名为multiply
的自定义函数,接受两个参数x
和y
,并返回它们的乘积。在awk
程序中,我们可以通过调用该函数来实现具体的计算。
split
在Shell脚本中,split
是一个字符串处理函数,用于将一个字符串分割成子字符串,并将子字符串存储在一个数组中。下面是关于split
函数的详细解释:
-
语法:
split(string, array, separator)
string
:要分割的字符串。array
:存储分割后的子字符串的数组。separator
:分割字符串的分隔符。可以是一个固定的字符或正则表达式。
-
功能:
split
函数将字符串按照指定的分隔符分割成多个子字符串,并将这些子字符串存储在数组中。分割后的子字符串可以根据索引访问,从1开始。 -
示例:
下面是一个使用split
函数的示例:str="apple,banana,orange" split(str, arr, ",") print arr[1] # 输出 "apple" print arr[2] # 输出 "banana" print arr[3] # 输出 "orange"
上面的示例中,
str
是要分割的字符串,arr
是存储分割后子字符串的数组。使用逗号作为分隔符对字符串进行分割,然后可以通过索引访问数组中的元素。 -
分隔符:
- 分隔符可以是一个固定的字符,例如空格、逗号、冒号等。
- 分隔符也可以是一个正则表达式,用于更复杂的字符串分割。例如,使用空格和逗号作为分隔符:
split(str, arr, "[ ,]")
。
-
返回值:
split
函数不返回任何值,它直接将分割后的子字符串存储在数组中。因此,它没有在条件语句中使用。 -
注意事项:
- 如果数组已经包含元素,调用
split
函数会覆盖数组中的现有内容。 - 如果分割后的子字符串数量超过数组的大小,额外的子字符串将被丢弃。
- 如果数组已经包含元素,调用
总结一下,split
函数是Shell脚本中用于分割字符串的函数。它将字符串按照指定的分隔符分割成多个子字符串,并将子字符串存储在一个数组中,以便进一步处理和使用。
substr
在Shell脚本中,substr
是一个用于提取字符串子串的函数。它允许你从一个字符串中选择指定位置的字符子串。下面是关于substr
函数的详细解释:
-
语法:
substr(string, start [, length])
string
:要提取子串的源字符串。start
:子串的起始位置。如果是正数,则表示从左到右的索引;如果是负数,则表示从右到左的索引(-1表示最后一个字符)。length
:可选参数,表示子串的长度。如果省略,则提取从起始位置到字符串末尾的所有字符。
-
功能:
substr
函数用于提取字符串中的一个子串,可以根据起始位置和长度来确定提取的子串范围。 -
示例:
下面是一些使用substr
函数的示例:str="Hello, World!" echo $(substr(str, 1, 5)) # 输出 "Hello" echo $(substr(str, 8)) # 输出 "World!" echo $(substr(str, -6)) # 输出 "World!"
上面的示例中,
str
是源字符串,使用substr
函数从中提取了不同的子串。第一个示例提取了从索引1开始的前5个字符,第二个示例提取了从索引8开始到字符串末尾的所有字符,第三个示例提取了从右侧数第6个字符到末尾的所有字符。[root@localhost ~]# echo "hunan|shaoyang|dongkou|gaosha"|awk -F "|" '{print substr($2,1,4)}' shao
该命令的作用是对字符串"hunan|shaoyang|dongkou|gaosha"进行分割,并输出截取的第二个字段的前4个字符。
-
注意事项:
- 起始位置和长度都可以是变量。
- 如果起始位置超过了字符串的长度,
substr
函数将返回空字符串。 - 如果起始位置为负数且绝对值超过了字符串的长度,
substr
函数将返回整个字符串。 - 如果长度为负数,
substr
函数将从起始位置向前提取相应长度的子串。
总结一下,substr
函数是Shell脚本中用于提取字符串子串的函数。它允许你根据起始位置和长度从一个字符串中选择指定位置的字符子串。这个函数在字符串处理和提取特定信息时非常有用。
使用
输出/etc/passwd文件中第一段字段中含有s的字符长度
[root@localhost 5-16]# awk -F: '$1 ~ /s/{print length($1)}' /etc/passwd
输出/etc/passwd文件中第二个字段长度为0的用户名,以及当前的日期
[root@localhost /]# awk -F: 'length($2) == 0 {print $1,"密码为空",strftime("%D",systime())}' /etc/passwd
将执行ls -l
命令,并将命令的输出打印到标准输出。
awk '{ system("ls -l") }' file.txt
awk和grep结合使用
awk '{ status = system("grep -q pattern file.txt") } END { print "Exit status:", status }' file.txt
上面的命令将执行grep -q pattern file.txt
命令,并将命令的退出状态码保存在status
变量中。在END
块中,它打印出命令的退出状态码。
需要注意的是,system
函数返回的是命令的退出状态码,而不是命令的输出。通常,命令的退出状态码为0表示成功,非零值表示失败或错误。如果你需要获取命令的输出,可以使用管道(|
)将命令的输出传递给awk,或者将命令的输出重定向到文件。
在system
函数中使用变量和字符串拼接来构建要执行的命令,以实现动态的命令执行。
awk -v pattern="$pattern" '{ system("grep " pattern " file.txt") }' file.txt
上面的命令中,$pattern
是一个awk变量,它的值在命令执行之前被替换到命令中。
[root@localhost /]# awk -F: '{system("mkdir -p /yan/dong/sc"$1)}' /etc/passwd
该命令的作用是根据/etc/passwd
文件的每一行的第一个字段,在/yan/dong
路径下创建相应的子目录,并将/etc/passwd
文件中的每个用户的用户名作为子目录名。
awk中的system
函数用于执行命令并返回命令的退出状态码,它可以在awk脚本中实现与操作系统的交互。
awk
函数的使用可以根据具体需求来选择合适的内置函数或自定义函数,以实现对文本的处理和转换操作。
awk中的if和for
当在Shell中使用awk时,if
语句和for
循环是非常有用的控制结构。它们允许您在awk脚本中执行条件判断和循环迭代操作。下面是对awk中if
语句和for
循环的超详细介绍和使用示例。
if语句
if
语句允许您根据条件来执行不同的操作。
语法:
单分支
if (condition) statement
双分支
if (condition) statement1;statement2
多分支
if (条件) {
操作
}
else if {
操作
}
else{
操作
}
示例:
# 根据条件判断输出结果
awk '{ if ($1 > 10) { print $1 " is greater than 10" } else { print $1 " is less than or equal to 10" } }' file.txt
在上述示例中,if
语句用于判断第一个字段是否大于10,如果大于10,则输出相应的消息;否则输出另一个消息。
for循环
for
循环允许您对数据进行迭代处理。
语法:
for (变量 in 数组/字符串) {
操作
}
示例:
# 循环遍历数组元素并输出
awk 'BEGIN { fruits["apple"]="red"; fruits["banana"]="yellow"; fruits["orange"]="orange"; for (fruit in fruits) { print fruit " is " fruits[fruit] } }'
在上述示例中,for
循环用于遍历数组fruits
中的元素,并输出每个水果对应的颜色。
使用示例
下面是一个更完整的示例,结合了if
语句和for
循环,演示了如何在awk脚本中使用它们:
# 读取文件中的数字,并统计奇偶数的个数
awk '{
if ($1 % 2 == 0) {
even_count++
} else {
odd_count++
}
}
END {
print "Even numbers count: " even_count
print "Odd numbers count: " odd_count
}' numbers.txt
在上述示例中,通过if
语句判断每个数字是否为偶数,然后相应地增加even_count
或odd_count
变量的计数。最后,在END
块中打印统计结果。
这是一个简单的示例,但它展示了如何使用if
语句和for
循环在awk脚本中执行条件判断和循环迭代操作。根据实际需求,可以根据条件执行不同的操作,或对数据进行循环处理。
awk中的数组
在Shell脚本中,awk是一种功能强大的文本处理工具,它支持数组数据结构用于存储和处理数据。数组是一种有序的集合,可以通过索引来访问和操作其中的元素。下面是关于awk中数组的详细解释:
-
声明数组:在awk中,可以使用下面的语法来声明一个数组:
array_name[index] = value
其中,
array_name
是数组的名称,index
是数组元素的索引(唯一),value
是要存储的值。需要注意的是,awk中的数组是动态的,不需要提前指定数组的大小。 -
访问数组元素:可以使用下面的语法来访问数组中的元素:
array_name[index]
通过指定数组名称和索引,可以获取对应位置的元素的值。
-
遍历数组:可以使用
for
循环来遍历数组中的所有元素。下面是一个示例:awk 'BEGIN { array[1]="A"; array[2]="B"; array[3]="C"; for (i in array) { print array[i] } }'
上面的命令将输出数组中的所有元素,即"A"、“B"和"C”。
-
数组长度:可以使用内置的
length
函数来获取数组的长度。下面是一个示例:awk 'BEGIN { array[1]="A"; array[2]="B"; array[3]="C"; print "Array length:", length(array) }'
上面的命令将输出数组的长度,即3。
-
删除数组元素:可以使用
delete
关键字来删除数组中的元素。下面是一个示例:awk 'BEGIN { array[1]="A"; array[2]="B"; array[3]="C"; delete array[2]; for (i in array) { print array[i] } }'
上面的命令将删除数组中索引为2的元素,并输出剩余的元素。
-
多维数组:awk还支持多维数组,即可以在数组的索引中使用多个值。下面是一个示例:
awk 'BEGIN { array[1,1]="A"; array[1,2]="B"; array[2,1]="C"; for (i in array) { split(i, indices, SUBSEP); print indices[1], indices[2], array[i] } }'
上面的命令定义了一个二维数组,并遍历输出所有元素及其索引。
-
对数组求和
定义每个省的数据
[root@localhost /]# cat num.txt
山东 aa 20
山东 bb 89
湖南 aa 12
湖北 aa 123
湖南 bb 143
湖北 bb 23
山西 aa 66
山西 bb 99
对同省的数据求和,同时输出每个省份的总数据
[root@localhost /]# awk '{pro[$1] += $3}END {for(i in pro) print i,pro[i]}' num.txt
湖南 155
山西 165
山东 109
湖北 146
以上是关于awk中数组的详细解释。数组在awk中非常有用,可以用于存储和处理大量的数据,并进行各种操作,如遍历、添加、删除等。