Shell流编辑器awk

1 awk简介

awk 是一种强大的文本处理工具和编程语言,广泛用于处理和分析文本文件,尤其是结构化文本(如 CSV 文件和日志文件)。awk 提供了丰富的功能,可以对文本进行过滤、格式化和数据提取。

2 awk语法格式

awk [options] 'pattern { action }' file

· options:awk 的选项,用于设置输入输出格式、定义变量等。
· pattern:模式,用于匹配输入文本中的特定行。如果省略模式,awk 将应用动作(action)到所有行。
· action:在匹配模式的行上执行的命令。用 {} 包围,可以包含多条命令。
· file:要处理的输入文件。awk 也可以处理来自管道的输入。

3 awk工作原理

  • 启动与初始化: awk 程序启动时,首先会执行 BEGIN 块中的语句(如果存在)。这通常用于初始化操作,如设置字段分隔符(FS)等。
    示例: awk ‘BEGIN { FS=“,”; OFS=“,” } { print $1, $2 }’ filename

  • 逐行读取输入: awk 从输入文件或标准输入中逐行读取数据。 每次读取一行,将其存储在模式空间(pattern space)中,同时会自动将行分割成字段(默认以空白字符为分隔符)。

  • 模式匹配: awk 对每一行应用给定的模式(pattern),如果模式匹配成功,则执行相应的动作(action)。 模式可以是正则表达式、表达式、特定的条件(如字段值的比较)等。
    示例: awk ‘/pattern/ { print $0 }’ filename

  • 执行动作: 如果模式匹配成功,awk 执行大括号 {} 内的动作。 动作可以是打印、赋值、算术运算、逻辑判断等。
    示例: awk ‘{ sum += $1 } END { print sum }’ filename

  • 结束与清理
    当所有行都处理完后,awk 执行 END 块中的语句(如果存在)。这通常用于输出总结结果或进行清理操作。
    示例: awk ‘END {print “Processing completed.” }’ filename

4 awk选项参数说明

4.1 -F fs 或 --field-separator fs

用于指定 awk 的输入字段分隔符(Field Separator)。默认情况下,awk 使用空白字符(空格或制表符)作为字段分隔符。但是,如果数据使用其他字符(如逗号、冒号、分号等),则可以使用 -F 选项来指定这些分隔符。

示例1 使用逗号作为分隔符
假设有一个文件 data.txt,内容如下:

Alice,30,New York
Bob,25,Los Angeles
Charlie,35,Chicago

打印每行的第一个字段和第三个字段。

awk -F ',' '{ print $1, $3 }' data.txt

输出:

Alice New York
Bob Los Angeles
Charlie Chicago

示例 2:使用冒号作为分隔符
文件 passwd.txt,内容如下:

root:x:0:0:root:/root:/bin/bashuser:x:1000:1000:User Name:/home/user:/bin/bash

打印用户名和主目录

awk -F ':' '{ print $1, $6 }' passwd.txt

输出:

root /root
user /home/user

示例 3:使用制表符作为分隔符
文件 data.tsv,内容如下:

Alice	30	New York
Bob	25	Los Angeles
Charlie	35	Chicago

打印每行的第一个字段和第三个字段

awk -F '\t' '{ print $1, $3 }' data.tsv

输出:

Alice New York
Bob Los Angeles
Charlie Chicago

示例4:设置内部字段分隔符
在 awk 脚本中,可以通过设置内置变量 FS 来指定字段分隔符,而不是命令行选项 -F。

BEGIN {
    FS = ","
}
{
    print $1, $3
}

将以上脚本保存为 script.awk,然后运行:

awk -f script.awk data.txt

输出与使用 -F ‘,’ 选项时相同。

4.2 -v var=value

说明:定义一个 awk 变量 var 并赋予初始值 value。

示例1:使用外部传入的阈值
假设有一个文件 data.txt,内容如下:

Alice 30
Bob 25
Charlie 35

打印年龄大于一个指定阈值的人的名字。这个阈值将通过命令行参数传递给 awk。

awk -v threshold=30 '$2 > threshold { print $1 }' data.txt

输出:

Charlie

在这个示例中,使用 -v threshold=30 定义了一个名为 threshold 的变量,并将其值设为 30。在 awk 脚本中,使用这个变量来过滤和打印年龄大于 30 的人的名字。

示例 2:结合多个外部变量
假设我们有一个更复杂的文件 data.txt,内容如下:

Alice 30 Sales
Bob 25 Marketing
Charlie 35 Engineering

过滤和打印特定部门中年龄大于某个值的人的名字。

awk -v threshold=30 -v dept="Sales" '$2 > threshold && $3 == dept { print $1 }' data.txt

输出:

Alice

在这个示例中,定义了两个变量 threshold 和 dept,并在 awk 脚本中使用它们来进行过滤。

示例 3:在脚本中使用外部变量
还可以将 -v 选项与 -f 选项结合使用,将外部变量传递给 awk 脚本文件。
假设有一个 awk 脚本文件 script.awk,内容如下:

$2 > threshold && $3 == dept { print $1 }

然后可以通过以下方式运行脚本,并传递外部变量:

awk -v threshold=30 -v dept="Sales" -f script.awk data.txt

输出与前面的示例相同。

示例 4:使用外部传入的变量控制输出格式
假设有一个文件 data.txt,内容如下:

Alice 30
Bob 25
Charlie 35

我们希望通过外部变量控制输出格式。

awk -v format="%-10s %d\n" '{ printf(format, $1, $2) }' data.txt

输出:

Alice      30
Bob        25
Charlie    35

· %-10s:左对齐的字符串,占用10个字符宽度。如果字符串长度不足10个字符,右边会补空格。
· %d:表示整数。

4.3 -f program-file 或 --file program-file:

说明:从指定文件中读取 awk 程序。

示例 1:简单的 awk 脚本
假设有一个包含以下内容的 data.txt 文件:

Alice 30 New York
Bob 25 Los Angeles
Charlie 35 Chicago

创建一个名为 script.awk 的文件,内容如下:

{ print $1, $3 }

然后运行以下命令:

awk -f script.awk data.txt

输出:

Alice New York
Bob Los Angeles
Charlie Chicago

示例 2:带有 BEGIN 和 END 块的 awk 脚本
创建一个名为 script.awk 的文件,内容如下:

BEGIN {
    print "Name\tCity"
    print "--------------"
}
{
    print $1, "\t", $3
}
END {
    print "Processing completed."
}

然后运行以下命令:

awk -f script.awk data.txt

输出:

Name    City
--------------
Alice    New York
Bob    Los Angeles
Charlie    Chicago
Processing completed.

示例 3:使用外部变量的 awk 脚本

创建一个名为 script.awk 的文件,内容如下:

$2 > threshold {
    print $1, $2
}

然后运行以下命令,将变量 threshold 传递给脚本:

awk -v threshold=30 -f script.awk data.txt

输出:

Charlie 35

4.4 -mf n 和 -mr n:

说明:设置最大记录数(-mr)或字段数(-mf)。这些选项通常在现代 awk 实现中不常用,因为默认设置已经足够大。

示例1:限制最大字段数

awk -mf 100 '{ print $1, $2 }' data.txt

设置 awk 在处理过程中允许的最大字段数为 100。如果一行中包含的字段数超过 100,awk 可能会报错或跳过处理。

示例2:限制最大记录数

awk -mr 1000 '{ print $1, $2 }' data.txt

设置 awk 在处理过程中允许的最大记录数为 1000。如果总记录数超过 1000,awk 可能会报错或停止处理。

4.5 --posix:

说明:使 awk 仅使用 POSIX 标准的功能,禁用所有 GNU 扩展。

使用 --posix 选项的目的

  • 移植性:确保脚本可以在不同的 awk 实现间无缝运行。
  • 可靠性:避免使用特定于 GNU awk 的功能,以防- 在非 GNU 环境中运行失败。
  • 一致性:强制遵循 POSIX 标准,保证脚本行为的一致性。

示例:

awk --posix '{ print $1 }' file.txt

–posix 选项是确保 awk 脚本具有良好移植性和一致性的有力工具。通过禁用 GNU 扩展,它确保脚本在任何符合 POSIX 标准的 awk 实现上都能正常运行。使用这个选项可以避免在不同系统间运行脚本时遇到的兼容性问题,是编写可移植和可靠 awk 脚本的最佳实践。

4.6 --lint:

说明:在脚本中启用警告,可以使用 --lint 选项来检查脚本的语法和潜在问题:

awk --lint -f script.awk input.txt

如果脚本中存在语法错误或不推荐的语法结构,awk 将会显示相应的警告或错误信息,帮助您在脚本执行之前发现并修正问题。
建议在开发和调试阶段使用 --lint 选项,以确保 awk 脚本的质量和可靠性。在生产环境中,通常不建议启用此选项,以避免不必要的性能损耗。

5 awk的print和prinft

  • print item1, item2, …
    作用:将指定的内容打印输出到标准输出(通常是终端或者重定向到文件),自动处理空格和换行符。
    假设有一个文件 data.txt 包含如下内容:
Alice 30
Bob 25
Charlie 35

要打印每行的第一个字段和第二个字段,可以这样使用 print:

awk '{ print $1, $2 }' data.txt

输出:

Alice 30
Bob 25
Charlie 35
  • printf format, item1, item2, …
    作用:根据指定的格式 (format) 输出内容。
    format 是一个控制输出格式的字符串,可以包含格式说明符(如 %s, %d, %f 等)和可选的宽度、精度等信息。
    可以根据需要输出固定格式的内容,例如对齐、保留小数位等。
    没有自动添加的换行符,需要手动控制输出格式和换行。
    示例:
    继续使用上面的 data.txt 文件,现在想要按照一定格式输出姓名和年龄:
awk '{ printf("Name: %-10s Age: %d\n", $1, $2) }' data.txt

输出:

Name: Alice      Age: 30
Name: Bob        Age: 25
Name: Charlie    Age: 35

6 awk内置变量

File1.txt中的内容如下:

123456   Hello World
678901   Goodbye

File2.txt内容如下:

Alice 30
Bob 25
Charlie 35
变量描述示例
$n当前记录的第 n 个字段,字段间由 FS 分隔root@root:~$ awk ‘{ print $1 }’ file1.txt
123456
678901
$0完整的输入记录root@root:~$ awk ‘{ print $0 }’ file1.txt
123456 Hello World
678901 Goodbye
ARGC命令行参数的数目,注意awk 命令本身占据了一个参数位置。root@root:~$ awk ‘BEGIN { print ARGC }’ file1 file2
3
ARGIND命令行中当前文件的位置(从0开始算)root@root:~$ awk ‘{ print ARGIND, FILENAME }’ file1.txt file2.txt
1 file1.txt
1 file1.txt
2 file2.txt
2 file2.txt
2 file2.txt
ARGVARGV 是一个数组,包含了命令行中传递给 awk 的所有参数,包括 awk 自身和文件参数。ARGV[0]:awk 自身的执行路径或名称。ARGV[1]:第一个文件参数值,即 file1.txt。root@root:~$ awk ‘BEGIN { print ARGV[1] }’ file1.txt
file1.txt
CONVFMT字段转换格式变量,只影响由 awk 进行数值转换时的输出格式。 它仅在将字符串转换为数字时才会起作用。数字转换格式(默认为 %.6g)root@root:~$ awk ‘BEGIN {
CONVFMT = “%.3f”;
str = “123.45678”;
num = str + 0;
print “Converted number:”, num;
}’
Converted number: 123.457
ENVIRON环境变量关联数组,ENVIRON[“PATH”]: 使用 ENVIRON 数组来访问环境变量,“PATH” 是环境变量的键名。root@root:~$ awk ‘BEGIN { print ENVIRON[“PATH”] }’
/usr/local/sbin:/usr/local/bin:/usr/sbin
ERRNO用于获取最后一个系统错误的描述。ERRNO 仅在出现系统相关错误时才会有值。root@root:~$ awk ‘BEGIN { print “Error:”, ERRNO }’
Error:
FIELDWIDTHS字段宽度列表,使用 -v 选项定义FIELDWIDTHS 变量,指定字段的长度,包含空格root@root:~$ awk -v FIELDWIDTHS=“5 10” ‘{ print $1, $2 }’ file1.txt
12345 6 Hello
67890 1 Goodby
FILENAME处理多文件时识别当前文件名root@root:~$ awk ‘{ print FILENAME }’ file1.txt file2.txt
file1.txt
file1.txt
file2.txt
file2.txt
file2.txt
FNRawk 的一个内置变量,表示当前正在处理的文件中的行号(从1开始计数)。root@root:~$ awk ‘{ print FNR, $0 }’ file1.txt file2.txt
1 123456 Hello World
2 678901 Goodbye
1 lice 30
2 Bob 25
3 Charlie 35
FS字段分隔符(默认为空格)root@root:~$ awk -F’:’ ‘{ print $1 }’ /etc/passwd
root
daemon
bin
sys
IGNORECASE{ IGNORECASE=1 }': IGNORECASE 变量为 1,表示忽略大小写进行匹配。root@root:~$ awk ‘BEGIN { IGNORECASE=1 } /g/ { print $0 }’ file1.txt
678901 Goodbye
NF当前行的字段数,以空格分割统计root@root:~$ awk ‘{ print NF }’ file1.txt
3
2
NR已经读出的记录数,即行号,从1开始root@root:~$ awk ‘{ print NR, $0 }’ file1.txt
1 123456 Hello World
2 678901 Goodbye
OFMT指定数字的输出格式(默认为 %.6g)root@root:~$ awk ‘BEGIN { OFMT = “%.2f”; print 10/3 }’
3.33
OFS输出字段分隔符(默认与 FS 相同)root@root:~$ awk -v OFS=‘,’ ‘{ print $1, $2 }’ file1.txt
123456,Hello
678901,Goodbye
ORS输出记录每行分隔符(默认是一个换行符)root@root:~$ awk ‘BEGIN { ORS = “\n\n” } { print $0 }’ file1.txt
123456 Hello World

678901 Goodbye

RLENGTH由 match 函数所匹配的字符串的长度root@root:~$ awk ‘match($0, /Good/) { print RLENGTH }’ file1.txt
4
RS记录分隔符(默认是一个换行符)root@root:~$ awk ‘BEGIN { RS = “\r\n” } { print $0 }’ file1.txt file2.txt
123456 Hello World
678901 Goodbye

lice 30
Bob 25
Charlie 35

RSTART由 match 函数所匹配的字符串在当前行中的起始位置root@root:~$ awk ‘match($0, /Good/) { print RSTART }’ file1.txt
10
SUBSEP数组下标分隔符(默认值是 /034)指定多维数组下标分隔符,例如 awk ‘BEGIN { SUBSEP = “-” } { array[“key1”, “key2”] = “value” }’

7 awk脚本

awk 脚本结构
一个典型的 awk 脚本结构如下:

BEGIN {
    # 初始化代码块
    # 例如,设置字段分隔符
    FS = ","
    print "Starting processing"
}

# 主处理代码块
# 对每一行进行处理
/pattern/ {
    # 如果匹配成功,执行这些动作
    print $1, $2
}

END {
    # 结束代码块
    # 例如,输出总结信息
    print "Processing completed."  
}

示例:
假设有一个包含以下内容的文件 data.txt:

apple 10
banana 20
cherry 30

现需要计算第一列为 “banana” 的第二列的总和

awk 'BEGIN { sum = 0 } 
     $1 == "banana" { sum += $2 } 
     END { print sum }' data.txt

BEGIN 块:初始化总和变量 sum 为 0。
主处理块:逐行读取文件,检查第一列是否为 “banana”。如果是,则将第二列的值加到 sum 中。
END 块:所有行处理完后,打印总和 sum。

8 awk条件语句与循环

  1. 条件语句(if-else)
    在 awk 中,条件语句的语法与其他语言类似,可以使用 if、else if 和 else 来根据不同的条件执行不同的代码块。
awk '{
    if ($1 > 50) {
        print $1, "is greater than 50";
    } else if ($1 < 50) {
        print $1, "is less than 50";
    } else {
        print $1, "is equal to 50";
    }
}' file.txt
  1. 循环结构
    awk 支持 for 循环和 while 循环来遍历数据或者重复执行代码块。

for循环:

awk 'BEGIN {
    for (i = 1; i <= 5; i++) {
        print "Count:", i;
    }
}'

while 循环:

awk 'BEGIN {
    i = 1;
    while (i <= 5) {
        print "Count:", i;
        i++;
    }
}'

这个示例也会输出从 1 到 5 的计数,但是使用了 while 循环。

9 awk数组

9.1 创建数组

awk 'BEGIN {
    # 创建一个名为 scores 的数组
    scores["Alice"] = 85;
    scores["Bob"] = 92;
    scores["Eve"] = 78;
    
    # 输出数组中的值
    print "Alice's score:", scores["Alice"];
    print "Bob's score:", scores["Bob"];
}' file.txt

9.2 多维数组

方式1:

awk 'BEGIN {
    # 创建一个二维数组
    grades["Alice"]["Math"] = 85;
    grades["Bob"]["Science"] = 92;
    grades["Alice"]["Science"] = 90;
    
    # 访问和输出多维数组中的元素
    print "Alice's Math score:", grades["Alice"]["Math"];
    print "Bob's Science score:", grades["Bob"]["Science"];
}' file.txt

方式2:

awk 'BEGIN {
    # 创建一个二维数组
    array["0,0"] = 100;
    array["0,1"] = 200;
    array["0,2"] = 300;
    array["1,0"] = 400;
    array["1,1"] = 500;
    array["1,2"] = 600;
    
    # 输出数组元素
    print "array[0,0] =", array["0,0"];
    print "array[0,1] =", array["0,1"];
    print "array[0,2] =", array["0,2"];
    print "array[1,0] =", array["1,0"];
    print "array[1,1] =", array["1,1"];
    print "array[1,2] =", array["1,2"];
}'

10 awk的函数

常用的awk内置函数如下

函数描述示例
length(str)返回字符串 str 的长度 length(“Hello”) 返回 5
substr(str, start, length)返回字符串 str 从 start 位置开始 length 长度的子串 substr(“Hello World”, 7, 5) 返回 “World”
index(str, search)返回 search 在字符串 str 中第一次出现的位置 index(“Hello World”, “World”) 返回 7
split(str, arr, sep)将字符串 str 按分隔符 sep 分割并存入数组 arr 中 split(“apple orange banana”, fruits, " ")
tolower(str)将字符串 str 转换为小写 tolower(“Hello”) 返回 “hello”
toupper(str)将字符串 str 转换为大写 toupper(“Hello”) 返回 “HELLO”
sprintf(fmt, expr)根据格式字符串 fmt 返回格式化后的字符串 sprintf(“%.2f”, 3.14159) 返回 “3.14”
int(expr)返回表达式 expr 的整数部分 int(3.99) 返回 3
sin(expr)返回角度 expr 的正弦值 sin(45) 返回正弦值
cos(expr)返回角度 expr 的余弦值 cos(60) 返回余弦值
log(expr)返回 expr 的自然对数(以 e 为底) log(10) 返回约 2.30259
sqrt(expr)返回 expr 的平方根 sqrt(25) 返回 5
rand()返回 [0, 1) 范围内的随机数 rand() 返回随机数
srand(seed)设置随机数种子 seed,用于 rand() 函数生成随机数 srand(1234)
getline var从输入文件中读取下一行,存入变量 var awk ‘BEGIN { getline line < “file.txt”; print line }’

详细函数说明可参考https://www.runoob.com/w3cnote/awk-built-in-functions.html

  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值