• 深入了解 Linux 中的 AWK 命令:文本处理的瑞士军刀


    简介

    Linux和Unix操作系统中,文本处理是一个常见的任务。AWK命令是一个强大的文本处理工具,专门进行文本截取和分析,它允许你在文本文件中查找、过滤、处理和格式化数据。本文将深入介绍Linux中的AWK命令,让你了解其基本用法和高级功能,以便更高效地处理文本数据。


    什么是AWK?

    AWK是一种处理文本文件的编程语言,它得名于其创始人Alfred Aho、Peter Weinberger和Brian Kernighan的姓氏首字母。AWK在命令行中使用,但更多是作为脚本来使用。AWK语言的强大之处在于它可以轻松地执行以下任务:

    1. 文本搜索和匹配:AWK可以在文本中搜索特定模式或关键字,并执行相应的操作。

    2. 数据提取和转换:它可以从文本中提取数据,并将其转换成不同的格式。

    3. 报告生成:AWK可以生成自定义格式的报告,适用于文本文件中的数据分析。

    4. 文本编辑:它可以用于编辑文本文件,添加、删除或修改文本行。


    AWK命令的执行过程

    基本语法

    awk  -v  var=value  '模式pattern { 动作action }'  filename
    •  awk的指令(patten+action)一定要用单引号 '... ' 括起来,动作一定要用花括号 { ... } 括起;
    • 模式可以是正则表达式、条件表达式或两种组合,如果模式是正则表达式要用定界符:/ ;
    • 动作之间用 ; 分开;
    • 定义自定义变量var,不需要接$符号;

    完整语法

    1. awk 'BEGIN{commands}pattern{commands}END{commands}' filename
    2. // 一个awk脚本通常由BEGIN,pattern,END语句块组成。三个部分都是可选的
    3. // 脚本通常是被单引号或双引号包住
    4. awk "BEGIN{commands}pattern{commands}END{commands}" filename

    或 

    1. awk 'BEGIN {
    2. # 初始化操作(处理数据前,执行的命令。例如,设置变量等)
    3. }
    4. /pattern1/ {
    5. # 当匹配到 pattern1 时执行的操作
    6. }
    7. /pattern2/ {
    8. # 当匹配到 pattern2 时执行的操作
    9. }
    10. END {
    11. # 输出最终结果或进行清理操作(处理数据后,执行的命令)
    12. }' filename

    执行过程的详细解释:

    1. awk 命令启动,并读取 filename 中的文本文件作为输入数据。如果未提供 filename,则默认从标准输入读取数据。
    2. BEGIN 块中的命令会在处理输入文件之前执行一次。这通常用于初始化变量、设置选项或执行其他预处理操作。
    3. awk 逐行读取输入文件,并将每一行拆分成字段(默认以空格为字段分隔符,可以使用 -F 选项指定其他分隔符)。
    4. pattern 块中的命令会在输入行匹配指定的模式时执行。模式可以是正则表达式或其他条件。如果存在多个 pattern 块,则会根据匹配的模式按顺序执行相应的命令。pattern语句块中的通用命令是最重要的部分,是可选的。如果没有提供pattern语句块,则默认执行END{ ... }。
    5. 在 pattern 块中,你可以访问字段和执行各种操作。例如,可以对字段进行计算、打印匹配行,或根据条件执行不同的操作。
    6. 当 awk 处理完输入文件的所有行后,它会执行 END 块中的命令。这通常用于输出最终结果、汇总数据或执行清理操作。
    7. awk 完成处理后,它会将结果打印到标准输出(通常是终端),除非您在脚本中显式使用 print 命令输出结果。

    基本用法

    在终端中,你可以使用以下的形式运行AWK命令:

    1. 最简单的形式

    awk [选项] '{ action }' file.txt

    这是最基本的形式,它会将 file.txt 文件的所有行都执行操作action。

    选项参数有:

    • -F 指定输入文件的字段分隔符,用于将输入行分割为字段。示例:
    1. awk -F, '{ print $1 }' data.txt
    2. 在这个示例中,-F, 指定逗号 , 作为字段分隔符,然后打印每行的第一个字段。
    • -v 定义一个用户定义变量,可以在脚本中使用。示例:
    1. awk -v name=John '{ print "Hello, " name "!" }' file.txt
    2. 在这个示例中,-v 选项定义了一个名为 name 的变量,并将其值设置为 "John",然后在脚本中使用它。
    • -f 指定包含 awk 脚本的文件,以便在执行时加载脚本。示例:
    1. awk -f myscript.awk file.txt
    2. 在这个示例中,-f 选项指定了一个名为 myscript.awk 的脚本文件,awk 将执行其中的命令。
    • -W [option]:启用某些扩展选项。示例:
    1. awk -W version
    2. 这将显示 awk 的版本信息。
    1. awk -W help
    2. 打印全部awk选项和每个选项的简短说明。
    • -E:切换为扩展正则表达式(ERE)模式匹配。示例:
    1. awk -E '/[0-9]+/ { print }' file.txt
    2. 在这个示例中,-E 选项允许使用扩展正则表达式模式,匹配包含数字的行。
    • -i inplace:在原始文件中进行原地编辑。示例:
    1. awk -i inplace '{ sub("old_pattern", "new_pattern") } 1' file.txt
    2. 这将在 file.txt 中查找并替换第一个匹配的 "old_pattern""new_pattern",并将结果写回原始文件。
    • -n:禁用默认的自动打印行为。示例:
    1. awk -n '/pattern/ { print }' file.txt
    2. 这将只打印包含 "pattern" 的行,不会自动打印所有行。
    • -FPOSIX:使用 POSIX 标准的字段分隔符。示例:
    1. awk -FPOSIX '{ print $1 }' data.txt
    2. 这将使用 POSIX 标准的字段分隔符进行字段分割。
    • -Wlint:启用 lint 模式,用于检查 awk 脚本中的潜在问题。示例:
    1. awk -Wlint -f myscript.awk file.txt
    2. 这将启用 lint 模式并检查 myscript.awk 中的潜在问题。
    • -Wsource=program-text:允许在命令行上直接提供 awk 脚本。示例:
    1. awk -Wsource='{ print "Hello, World!" }' file.txt
    2. 这将在命令行上直接提供 awk 脚本,然后在 file.txt 上执行。

    2. 指定字段分隔符的形式

    awk -F, '{ print $1, $2 }' data.csv

    这个形式使用了 -F 选项来指定字段分隔符为逗号,然后打印每行的第一个和第二个字段。

    3. 使用脚本文件的形式

    awk -f myscript.awk file.txt

    在这个形式中,awk 使用了 -f 选项,后面跟着一个包含 awk 脚本的文件 myscript.awk,并对 file.txt 文件执行该脚本中定义的操作。

    4. 设置变量的形式

    awk -v var=value '{ print var, $1 }' file.txt

    这个形式使用了 -v 选项来设置一个变量 var,然后将其与文件中的第一个字段一起打印。

    5. 条件过滤的形式

    awk 'pattern { action }' filename
    • pattern:是一个正则表达式或条件,用于匹配文本中的行。
    • action:是在满足条件的行上执行的操作。
    • filename:是要处理的文本文件的名称。

    这个形式使用了正则表达式模式 /pattern/,只对文件中匹配该模式的行执行操作action。

    以下是一个示例,演示了如何使用AWK命令查找并打印包含关键字的行:

    awk '/help/ { print }' english.txt
    

    这将在名为english.txt的文件中查找包含关键字"help"的行,并将它们打印到终端。

    其它

    有时候,AWK命令的形式的部分会有省略,有如下几种情况,举例说明:

    1. 只有模式没有动作,结果为显示$0($0 表示整行文本)

    awk '/chen/' scores.txt

     2. 只有动作没有模式,就直接执行动作

    who | awk '{print $2}'


    字段和分隔符

    内置变量$1

    AWK默认使用空格作为字段分隔符。在处理文本时,它将文本行分割成多个字段,你可以使用$1、$2、$3等特殊的内置变量来引用这些字段($0 表示整行文本),它们用于表示当前正在处理的输入行(或记录)的不同字段(列)的值。例如,$1代表文本中第一个字段,$2代表第二个字段,$n 表示第n个字段,以此类推。

    以下是一个示例,演示如何使用AWK命令提取文本行中的第二个字段:

    awk '{ print $2 }' english.txt 

    如果文本文件中的字段是以逗号、制表符或其他字符分隔的,你可以使用 -F 选项来指定分隔符。例如,如果文本以逗号分隔,您可以这样使用:

    awk -F, '{ print $2 }' english.txt 

    内置变量FS & OFS

     AWK 命令中,用于控制字段的输入和输出分隔符的内置变量:

    • 输入分隔符 FS (Field Separator):用于指定字段的输入分隔符。默认情况下,awk 使用空格作为字段分隔符,但你可以使用 -F 选项或在 BEGIN 块中设置 FS 来指定不同的字段分隔符。
    • 输出分隔符 OFS (Output Field Separator):用于指定字段的输出分隔符。默认情况下,OFS 为空格,这意味着 awk 在打印输出时会在字段之间插入空格。你可以在 BEGIN 块中设置 OFS 来指定不同的输出字段分隔符。

    示例 1

    以下是一个示例,演示了如何在 awk 中使用 FS 和 OFS 来控制字段分隔和输出分隔:

    假设有一个 CSV 文件 data.csv 包含以下内容:

    1. Alice,90,88,92
    2. Bob,78,85,80
    3. Charlie,92,94,89

    可以使用以下 awk 命令来读取该文件,将逗号作为字段分隔符,并在输出中使用制表符作为字段分隔符:

    1. awk 'BEGIN {
    2.     FS = ","
    3.     OFS = "\t"  # 使用制表符作为输出字段分隔符
    4. }
    5. {
    6.     print $1, $2, $3, $4
    7. }' data.csv

     命令也可以这样写入(注意:在 AWK 中,可以在同一行上包含多个命令,并使用分号来分隔它们)

    awk 'BEGIN { FS = ","; OFS = "\t" } { print $1, $2, $3, $4 }' data.csv
    

    运行此命令将输出以下内容:

    在这个示例中,BEGIN 块中设置了 FS 为逗号,表示字段分隔符是逗号。然后,OFS 被设置为制表符,表示输出字段分隔符是制表符。这样,在输出时字段之间将使用制表符分隔。

    示例 2

     OFS 设置为逗号,输出重定向到csv文件

    awk -F: 'OFS=","{print $1,$3,$5}' /etc/passwd > passwd.csv

    运行此命令将输出以下内容:

    补充

    在AWK中,使用-F选项来指定字段分隔符,可以使用单引号、双引号或不使用引号,它们的效果是相同的。这是因为在命令行中,单引号或双引号通常用于防止特殊字符的解释或空格的分隔。


    内置变量

    AWK还提供了一些内置变量,用于执行更复杂的操作:

    • NR:代表记录数(行号),用于跟踪处理的行数。
    1. awk 'NR >= 3 && NR <= 5{print NR,$0}' /etc/passwd
    2. # 或写成下面这种形式,作用结果相同。因为在 AWK 中,逗号“ , ”用于表示一个范围,其中 NR==3,NR==5 表示从行号 3 到行号 5 的范围。
    3. awk 'NR==3,NR==5{print NR,$0}' /etc/passwd

    解释:这个命令会打印第三到第五行的行号和内容。使用 NR(行号)内置变量来检查每一行的行号。NR >= 3 表示行号大于等于3,NR <= 5 表示行号小于等于5。

    • NF:代表字段数,用于确定每行有多少个字段。
    awk '{ print NF, $0 }' filename.txt

    解释:上述命令将打印每行的字段数和内容。

    • FS:代表字段分隔符,用于指定字段之间的分隔符,默认是任何空格。
    awk 'BEGIN { FS = "," } { print $2 }' english.txt

    解释:上面的示例使用BEGIN块来在AWK脚本开始执行之前设置FS变量为逗号。然后打印每行的第二个字段,AWK会使用FS来解释输入数据中的字段分隔符。

    • RS:代表记录分隔符,用于指定记录之间的分隔符。记录分隔符是一个用于分隔文本中记录(行)的特殊字符或字符串。默认情况下,RS的值是一个换行符(\n),这意味着每行文本都被视为一个记录。可以通过设置RS来更改记录分隔符,以根据需要处理多行记录,例如RS = "\n\n"表示两个连续的换行符分隔记录。
    awk 'BEGIN { RS = "\n\n" } { print NR, $0 }' filename.txt

    解释:上述命令将以双换行符作为记录分隔符,并打印每个记录的行号和内容。

    补充:记录分隔符(RS)用于分隔不同的记录(行),字段分隔符(FS)用于分隔记录内的不同字段。通过设置这两个变量,你可以适应不同的文本数据格式和处理要求。 

    • $n : 当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。
    awk '{ if (NF > 16) print NR, $1, $NF }' english.txt 

    这将打印出那些包含16个或更多字段的行的:行号NR、第一个字段$1和最后一个字段$NF。

    • $0 : 这个变量包含执行过程中当前行的文本内容。
    awk '/pattern/ { print $0 }' filename.txt

    解释:上述命令将打印包含特定模式pattern的整行内容。

    • ARGC : 命令行参数的数目。
    awk 'BEGIN{ print ARGC }' sshd.txt data.csv 
    

     解释:上述命令将打印命令行参数的数目,包括文件名。

    • ARGIND : 命令行中当前文件的位置(从0开始算)。
    awk '{ print ARGIND, FILENAME }' file1.txt file2.txt

    解释:上述命令将打印当前处理的文件位置和文件名。

    • ARGV : 包含命令行参数的数组。
    awk 'BEGIN { for (i in ARGV) print ARGV[i] }' file1.txt file2.txt

    解释:上述命令将打印命令行参数数组中的所有参数。

    • CONVFMT : 数字转换格式(默认值为%.6g)。
    awk 'BEGIN { CONVFMT = "%.2f"; print 1.234567 }'

    解释:上述命令将设置数字输出格式为两位小数并打印。

    • ENVIRON : 环境变量关联数组。
    awk 'BEGIN { print ENVIRON["HOME"] }'

    解释:上述命令将打印用户的家目录。

    • ERRNO : 最后一个系统错误的描述。
    awk 'BEGIN { if (system("nonexistent_command")) print "Error:", ERRNO }'

    解释:上述命令将尝试执行一个不存在的命令,并打印系统错误描述。

    • FIELDWIDTHS : 字段宽度列表(用空格键分隔)。
    awk 'BEGIN { FIELDWIDTHS = "3 5 2"; print $1, $2, $3 }' filename.txt

    解释:上述命令将按指定的字段宽度解析输入文本。

    • FILENAME : 当前输入文件的名。
    awk '{ print FILENAME }' file1.txt file2.txt

    解释:上述命令将打印当前处理的文件名。

    • FNR : 同NR :,但相对于当前文件。
    awk '{ print FNR, $0 }' file1.txt file2.txt

    解释:上述命令将打印相对于当前文件的行号和内容。

    • IGNORECASE : 如果为真,则进行忽略大小写的匹配。
    awk 'BEGIN { IGNORECASE = 1 } /pattern/ { print $0 }' filename.txt

    解释:上述命令将进行大小写不敏感的模式匹配。

    • OFMT : 数字的输出格式(默认值是%.6g)。
    awk 'BEGIN { OFMT = "%.3f"; print 1.234567 }'

    解释:上述命令将设置数字输出格式为三位小数并打印。 

    • OFS : 输出字段分隔符(默认值是一个空格)。
    awk 'BEGIN { FS = ","; OFS = "\t" } { print $1, $2, $3, $4 }' data.csv
    


    解释:在这个示例中,BEGIN 块中设置了 FS 为逗号,表示字段分隔符是逗号。然后,OFS 被设置为制表符,表示输出字段分隔符是制表符。这样,在输出时字段之间将使用制表符分隔。

    • ORS : 输出记录分隔符(默认值是一个换行符)。
    awk 'BEGIN { ORS = "\n\n" } { print $0 }' filename.txt

    解释:上述命令将以双换行符作为输出记录分隔符。

    • RSTART : 由match函数所匹配的字符串的第一个位置。
    echo "Hello, World" | awk 'match($0, /World/) { print RSTART }'

    解释:上述命令使用match函数在输入文本中查找字符串"World"并打印匹配的起始位置,RSTART变量将包含匹配字符串的第一个字符在输入文本中的位置。

    • RLENGTH : 由match函数所匹配的字符串的长度。
    echo "Hello, World" | awk 'match($0, /World/) { print RLENGTH }'

    解释:上述命令使用match函数在输入文本中查找字符串"World"并打印匹配的长度,RLENGTH变量将包含匹配字符串的长度。

    • SUBSEP : 数组下标分隔符(默认值是34)。
    awk 'BEGIN { print SUBSEP }'

    解释:上述命令在AWK的BEGIN块中打印SUBSEP的值,默认情况下,SUBSEP的值是ASCII码值为34的字符(双引号),它用于分隔多维数组中的索引,以便创建关联数组的复杂结构。


    AWK 操作符

    AWK 是一种强大的文本处理工具,支持各种操作符,用于执行条件检查、数学运算、字符串处理和模式匹配等操作。以下是一些常见的 AWK 操作符及其示例:

    算术操作符

    • +:加法操作。
    • -:减法操作。
    • *:乘法操作。
    • /:除法操作。
    • %:取模操作。

    示例:这个命令计算每行中的第一个字段和第二个字段的和,并将结果打印出来。

    awk '{ result = $1 + $2; print result }' data.txt

    关系操作符

    • ==:相等。
    • !=:不相等。
    • <:小于。
    • >:大于。
    • <=:小于等于。
    • >=:大于等于。

    示例:这个命令检查第三个字段是否等于 1001,如果是,则打印相应行的第一个字段。

    awk '$3 == 1001 { print $1 }' data.txt

    逻辑操作符

    • &&:逻辑与。
    • ||:逻辑或。
    • !:逻辑非。

    示例:这个命令检查第二个字段是否大于 50 ,并且第三个字段是否小于 90,如果满足条件,则打印相应行的第一个字段。

    awk '$2 > 50 && $3 < 90 { print $1 }' data.txt

    赋值操作符

    • =:赋值操作。

    示例:这个命令计算每行中的第一个字段和第二个字段的和,并将结果赋值给变量 total,然后打印出总和。

    awk '{ total = $1 + $2; print "Total: " total }' data.txt

    增量/减量操作符

    • ++:增量操作。
    • --:减量操作。

    示例:这个命令用于计算文件中的总行数。每次处理一行时,会将变量 count 增加 1。在处理结束时(END 部分),打印总行数。

    awk '{ count++; } END { print "Total Lines: " count }' data.txt

    模式匹配操作符:

    • ~:匹配模式。
    • !~:不匹配模式。

    示例:这个命令匹配第四个字段是否包含特定模式(在示例中是 "pattern")。如果匹配成功,则打印相应行的第一个字段。

    awk '$4 ~ /pattern/ { print $1 }' data.txt

    字符串连接操作符

    • "":用于连接字符串。

    示例:这个命令将每行的第一个字段 $1和第二个字段 $2连接起来,形成完整的姓名,并打印出来。

    awk '{ fullName = $1 " " $2; print "Full Name: " fullName }' data.txt

    三元条件操作符

    • ? ::用于条件赋值。

    示例:这个命令根据第三个字段的值是否大于等于 90 来确定考试状态。如果条件成立,将 "Pass" 赋给变量 status,否则赋给 "Fail",然后打印学生的姓名和状态。

    awk '{ status = ($3 >= 90) ? "Pass" : "Fail"; print $1, status }' data.txt

    正则表达式

    正则表达式(Regular Expression,简称 Regex 或 Regexp)是一种强大的文本模式匹配工具,用于在文本中查找、匹配和操作符合特定模式的字符串。正则表达式由普通字符和特殊字符组成,它们可以形成复杂的匹配规则。正则表达式在 awk 命令中广泛应用,用于处理文本数据。

    在某些编程语言和工具中,正则表达式通常是用两个斜杠 / 括起来的,例如 JavaScript 和 Perl。这种斜杠括起来的格式用于表示正则表达式的开始和结束,

    下面介绍正则表达式中的常见字符,并提供在 awk 中的示例和解释:

    普通字符

    普通字母和数字

    正则表达式中的普通字母和数字通常表示它们自身。例如,字母 "a" 匹配字符 "a"。

    awk '/apple/ {print}' file.txt

    这个命令将打印包含匹配 "apple" 的文本行。

    空格和标点符号

    空格和大多数标点符号通常表示它们自身。例如,空格字符 " " 匹配空格。

    awk '/hello, world/ {print}' file.txt

    这个命令将打印包含 "hello, world" 的文本行。

    特殊字符

    .(点号):匹配任何单个字符(除了换行符 \n)

    awk '/a.b/ {print}' file.txt

    这个命令将打印包含 "a.b" 模式的所有文本行,其中 . 匹配任意字符。

    *(星号):匹配前一个元素零次或多次。

    awk '/ab*c/ {print}' file.txt

    这个命令将打印包含 "abc"、"abbc"、"abbbc" 等模式的文本行,星号 * 表示匹配 "b" 零次或多次。

    +(加号):匹配前一个元素一次或多次。

    awk '/ab+c/ {print}' file.txt

    这个命令将打印包含 "abc"、"abbc"、"abbbc" 等模式的文本行,加号 + 表示匹配 "b" 一次或多次。

    ?(问号):匹配前一个元素零次或一次。

    awk '/ab?c/ {print}' file.txt

    这个命令将打印包含 "ac"、"abc" 模式的文本行,问号 ? 表示匹配 "b" 零次或一次。

    [ ](字符类):匹配括号内的任意一个字符。

    awk '/[aeiou]/ {print}' file.txt

    这个命令将打印包含任何元音字母的文本行。

    [^](否定字符类):正则表达式中的一个元字符,它有两种不同的含义,取决于它在正则表达式中的位置:

    在正则表达式的开头:当 ^ 出现在正则表达式的开头时,它表示匹配输入文本的开头位置。例如,正则表达式 ^abc 表示匹配以 "abc" 开头的字符串。这并不表示否定或排除其他内容,而是确保匹配从字符串的开头开始。

    假设有一个名为 data.txt 的文本文件包含以下内容:

    1. apple pie
    2. banana split
    3. cherry cake

    现在,想找到以 "apple" 开头的行,可以使用 ^ 来匹配这些行,如下所示:

    awk '/^apple/ { print }' data.txt

    /^apple/ :是正则表达式,其中 ^ 表示匹配以 "apple" 开头的行。
    { print } :是一个动作块,用于打印匹配的行。

    运行此命令后,AWK将匹配以 "apple" 开头的行,并打印它们的内容:

    在字符集 [ ] 内的开头:如果 ^ 出现在字符集 [ ] 内的开头,它表示否定或排除字符。例如,[^0-9] 表示匹配除了数字 0 到 9 以外的任何字符。

    awk '/[^0-9]/ {print}' file.txt

    这个命令将打印包含任何非数字字符的文本行。

    { } :用于控制匹配模式的重复次数。

    awk '/[0-9]{2,4}/ {print}' file.txt
    

    这个命令将匹配包含2到4个连续数字字符的文本行,并打印它们。

    ( )(分组):用于创建子表达式,允许对子表达式应用量词。当使用 () 分组时,它们允许你将多个元素视为一个整体,并对该整体应用量词或其他正则表达式操作符。

    awk '/(abc)+/ {print}' file.txt

    这个命令将打印包含 "abc"、"abcabc"、"abcabcabc" 等重复模式的文本行。其他示例:

    1. 匹配电话号码:使用 () 分组来匹配电话号码的不同格式。

    awk '/(\d{3}-)?\d{3}-\d{4}/ {print}' file.txt

    这个示例中,正则表达式 (\d{3}-)?\d{3}-\d{4} 匹配形如 "123-456-7890" 或 "456-7890" 的电话号码。括号内的 (\d{3}-)? 表示可选的区号。

    2. 匹配HTML标签:使用 () 分组来匹配HTML标签。

    awk '/<(\w+)>.+<\/\1>/ {print}' file.html 

    此示例中,正则表达式 <(\w+)>.+<\/\1> 可以匹配类似于

    这是段落

    的HTML标签,其中 (\w+) 匹配标签名,并使用 \1 来引用匹配的标签名,确保起始和结束标签相匹配。 

    3. 匹配IPv4地址:使用 () 分组来匹配IPv4地址。

    awk '/((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/ {print}' file.txt

    这个正则表达式 ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) 用于匹配IPv4地址,括号内的部分 (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\. 匹配一个IP地址的一部分。

    |(分支:用于分支匹配,匹配 | 左边或右边的表达式。

    awk '/apple|banana/ {print}' file.txt

    这个命令将打印包含 "apple" 或 "banana" 的文本行。

    ^(脱字符):匹配文本的开头。

    awk '/^start/ {print}' file.txt

    这个命令将打印以 "start" 开头的文本行。

    $(美元符号):匹配文本的结尾。

    awk '/end$/ {print}' file.txt

    这个命令将打印以 "end" 结尾的文本行。

    \(反斜杠):用于转义特殊字符。

    awk '/\./ {print}' file.txt

    这个命令将打印包含句号 "." 的文本行,反斜杠 \ 用于转义特殊字符。

    \d:匹配数字字符,等同于 [0-9]。

    awk '/[0-9]/ {print}' file.txt

    这个命令将打印包含数字字符的文本行。

    注意: 在标准的 awk 中,\d、\D 和 \w 也不是合法的正则表达式转义序列,因此不能直接在 awk 中使用。这些转义序列通常用于其他支持 Perl 兼容正则表达式语法的工具和编程语言,而不是标准的 awk。因为 \d 不是 awk 的标准正则表达式语法,所以在标准的awk 中将不会正常工作。应该使用 [0-9] 来匹配数字字符。

    \D:匹配非数字字符,等同于 [^0-9]。

    awk '/[^0-9]/ {print}' file.txt

    这个命令将打印包含非数字字符的文本行。

    \w:匹配字母、数字和下划线字符,等同于 [A-Za-z0-9_]。

    awk '/[A-Za-z0-9_]/ {print}' file.txt

    这个命令将打印包含字母、数字或下划线字符的文本行。

    [A-Za-z0-9_] 是一个字符类(character class),用于匹配一组字符。这个字符类包含了大写字母 A 到 Z、小写字母 a 到 z、数字 0 到 9,以及下划线字符 _。每个字符类中的字符都表示自身,即它们匹配它们自己。即:

    1. [A-Za-z]:这部分表示匹配任何一个字母,无论是大写还是小写字母。
    2. [0-9]:这部分表示匹配任何一个数字。
    3. _:这部分表示匹配下划线字符。

    \W:匹配非字母、非数字和非下划线字符,等同于 [^A-Za-z0-9_]。

    awk '/[^A-Za-z0-9_]/ {print}' file.txt

    这个命令将打印包含非字母、非数字或非下划线字符的文本行。

    \s:匹配空白字符,包括空格、制表符、换行符等。

    awk '/[ \t]/ {print}' file.txt

    这个命令将打印包含空格或制表符等空白字符的文本行。

    \S:匹配非空白字符。

    awk '/\S/ {print}' file.txt

    这个命令将打印包含非空白字符的文本行。

    \<\> :用于单词边界的匹配。\< 匹配单词的开头位置,\> 匹配单词的结尾位置

    awk '/\ file.txt

    这个命令用 \< 来匹配以 "apple" 开头的单词。当 awk 扫描文本文件 file.txt 时,它会打印包含以 "apple" 开头的单词的文本行。

    awk '/orange\>/ {print}' file.txt

    这个命令用用 \> 来匹配以 "orange" 结尾的单词。如果文件中有 "I like orange",那么这个命令会匹配并打印这个文本行,因为它包含以 "orange" 结尾的单词。

    不同特殊字符的结合使用

    正则表达式中的特殊字符可以结合一起使用,以创建更复杂的匹配模式。以下是一些常见的示例,演示了如何结合使用不同的特殊字符:

    结合字符类和量词:你可以将字符类和量词组合在一起,以匹配多个特定类型的字符。

    • [A-Za-z]+:匹配一个或多个字母字符。
    awk '/[A-Za-z]+/ {print}' file.txt

    这个命令将匹配包含一个或多个字母字符的文本行,诸如 abc、Hello、RegularExpressions 等包含一个或多个英文字母的文本。并打印它们。

    • [0-9]{2,4}:匹配两到四个数字字符。
    awk '/[0-9]{2,4}/ {print}' file.txt  #也可以用这种形式:awk '/[012345689]{2,4}/ {print}' file.txt

    这个命令将匹配包含2到4个连续数字字符的文本行,并打印它们。

    结合分组和分支:你可以使用分组和分支来匹配多个模式中的一个。

    • (apple|banana):匹配 "apple" 或 "banana"。
    awk '/(apple|banana)/ {print}' file.txt

    这个命令将匹配包含 "apple" 或 "banana" 的文本行,并打印它们。 

    • (red|blue|green) apple:匹配 "red apple"、"blue apple" 或 "green apple"。
    awk '/(red|blue|green) apple/ {print}' file.txt

     这个命令将匹配包含 "red apple"、"blue apple" 或 "green apple" 的文本行,并打印它们。

    结合锚点和字符类:结合锚点和字符类可以精确定位匹配位置。

    • ^[\d]{3}-[\d]{2}-[\d]{4}$:匹配形如 "123-45-6789" 的美国社会安全号码。
    awk '/^[0-9]{3}-[0-9]{2}-[0-9]{4}$/ {print}' file.txt
    

     这个命令将匹配形如 "123-45-6789" 的美国社会安全号码,确保整个文本行都匹配该模式。

    结合转义字符和特殊字符:转义字符用于匹配特殊字符本身。

    • \.:匹配句号 "."。
    awk '/\./ {print}' file.txt

    这个命令将匹配包含句号 "." 的文本行,并打印它们。句号在正则表达式中是一个特殊字符,所以它需要通过反斜杠进行转义。

    • \*:匹配星号 "*"。
    awk '/\*/ {print}' file.txt

     这个命令将匹配包含星号 "*" 的文本行,并打印它们。星号在正则表达式中通常用于表示零个或多个重复,所以它需要通过反斜杠进行转义。

    结合量词和分组:结合量词和分组可以匹配特定数量的重复模式。

    • (ab)+:匹配一个或多个 "ab" 的连续出现。
    awk '/(ab)+/ {print}' file.txt

    这个命令将匹配包含一个或多个连续出现的 "ab" 的文本行,并打印它们。

    • (xyz){3}:匹配连续出现的 "xyz" 三次。
     awk '/(xyz){3}/ {print}' file.txt

    这个命令将匹配包含连续出现的 "xyz" 三次的文本行,并打印它们。

    这些示例演示了如何结合使用正则表达式中的特殊字符和操作符,以创建更复杂的匹配模式,以满足不同的匹配需求。正则表达式的强大之处在于,它允许您构建非常灵活的模式来匹配各种文本数据。


    其他应用

    AWK不仅支持基本的文本处理,还可以进行其他的一些数据操作,如计算、条件语句、循环等。以下是一些应用的示例:

    awk命令的引用shell变量

    在 AWK 命令中引用 shell 变量可以通过 -v 选项来实现。这允许你将 shell 变量传递给 AWK 脚本,并在脚本内部使用。

    下面是一个示例,演示了如何引用 shell 变量:

    1. # 在 shell 中定义一个变量
    2. name="John" 
    3. # 使用 AWK 命令引用 shell 变量
    4. awk -v new_name="$name" '{ print new_name }' file.txt

    在这个示例中,首先在 shell 中定义了一个名为 name 的变量,并将其值设置为 "John"。然后,通过 -v 选项将 shell 变量 name 传递给了 AWK 脚本 ($ 符号用于引用 shell 变量,获取变量的值),并将其存储在 AWK 变量 name 中。在 AWK 脚本内部,我们使用 new_name 变量来引用 shell 变量的值,然后在 AWK 脚本内部使用 new_name 变量来打印相应行的值。

    这样,您可以将 shell 变量的值传递给 AWK 脚本,以在 AWK 脚本中使用它们,实现更灵活的文本处理和数据操作。

    定义内部变量接收外部变量:

    1. var1="aaa"
    2. var2="bbb"
    3. echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2

    补充

    需注意区分以下两个命令的区别: 

    awk -v name=John '{ print name }' file.txt

    和 

    1. name="John" # 定义shell变量name
    2. awk -v new_name="$name" '{print new_name}' file.txt

    这两个命令中,主要区别在于name=John和new_name="$name"这两行的定义和传递方式。

    第一个命令在AWK命令行中直接定义了一个AWK变量name,并将其值设置为"John"。然后,在AWK脚本内部,直接使用name变量,不需要双引号。

    第二个命令首先在shell中定义了一个shell变量name,并将其值设置为"John"。然后,使用-v选项将shell变量name的值传递给了AWK脚本,并将其存储在AWK变量new_name中。

    关于双引号的问题:

    new_name="$name" 中的双引号用于将shell变量$name的值传递给new_name,以确保如果name包含空格或特殊字符时,值仍然保持完整。在AWK脚本内,如果直接使用name而不是"$name",如果name包含空格或特殊字符,可能会导致意外的结果。所以,使用双引号是一种良好的做法,以确保变量值的完整性。

    next 语句

    next 是AWK中的控制流语句,用于跳过当前行并移动到下一行进行下一轮的处理。next 语句通常与条件语句结合使用,以便在满足特定条件时跳过当前行的处理。

    下面是 next 语句的基本语法:

    1. condition { 
    2.     # 在满足条件时执行的操作
    3.     # ...
    4.     next  # 跳过当前行,继续处理下一行
    5. }
    • 示例 1:使用 next 跳过包含特定关键字的行

    假设有一个名为 data.txt 的文本文件包含以下内容:

    1. apple
    2. banana
    3. cherry
    4. date
    5. grape

    我们想要在处理时跳过包含关键字 "date" 的行。我们可以使用 next 来实现这一点:

    awk '/date/ { next } { print }' data.txt

    /date/: 是正则表达式,用于匹配包含 "date" 的行。
    { next }:用于在正则表达式匹配的时侯,跳过当前行。/date/ { next }是第一个动作块
    { print }:用于打印未被跳过的行。{ print }是第二个动作块
    运行此命令后,将跳过包含 "date" 的行,打印其余行:

    1. apple
    2. banana
    3. cherry
    4. grape
    • 示例 2:使用 next 跳过特定字段的值小于 10 的行

    假设有一个包含数字的名为 numbers.txt 的文本文件,每行包含多个数字,用空格分隔:

    1. 5 12 8
    2. 15 30 20
    3. 7 9 11

    我们想要跳过包含字段值小于 10 的行。我们可以使用 next 来实现这一点:

    awk '{ for (i=1; i<=NF; i++) { if ($i < 10) { next } } print }' numbers.txt

    for (i=1; i<=NF; i++) { if ($i < 10) { next } } :这是一个for循环块,用于遍历每行中的每个字段。NF 表示字段的数量,而 $i 表示当前字段的值。在每个字段上执行以下操作:if ($i < 10) { next }是一个条件语句,检查当前字段的值是否小于 10。如果是,那么 next 语句将会跳过当前行,然后继续处理下一条记录。
    print:这是一个独立的动作块,用于打印未被跳过的记录(行)。在这个示例中,没有进一步的打印格式,因此它会将未被跳过的记录原样打印出来。
    运行此命令后,将跳过包含任何字段小于 10 的行,只打印字段值都大于或等于 10 的行:

    15 30 20 

    getline函数

    getline 是AWK中的内置函数,用于从文件或标准输入读取一行文本并将其存储到变量中。getline 函数具有灵活的用法,允许你从不同的输入源中读取数据。以下是 getline 函数的基本语法:

    1. getline [var]   # 从标准输入读取一行,将其存储到 var 中。
    2. getline [var] # 从文件中读取一行,存储到默认变量 $0 中。
    3. getline # 从文件中读取一行,存储到 var 中
    4. # [var] 可以省略,这样 getline 会将读取的数据存储在默认的 $0 变量中。
    5. # 所以,以下两种方式是等效的:
    6. getline var
    7. getline

    现在,让我们通过示例来详细解释 getline 的用法。

    假设有一个名为 data.txt 的文本文件,包含以下内容:

    1. This is line 1.
    2. This is line 2.
    3. This is line 3.

    我们将使用 getline 从文件中读取数据并进行处理。

    • 示例 1:从文件中读取一行并存储到变量中
    1. awk '{ getline line < "data.txt"; print "Read line from file: " line }' input.txt
    2. # 或
    3. awk '{
    4.     getline line < "data.txt"
    5.     print "Read line from file: " line
    6. }'

    getline 函数来从文件 data.txt 中读取一行文本,并将其存储在变量 line 中。然后,我们使用 print 命令将读取的行内容打印出来。

    运行此命令后,AWK会从 data.txt 文件中读取每行的内容并在每次循环中打印出来。

    • 示例 2:从标准输入读取一行
    1. awk '{
    2. print "Enter a line of text: "
    3. getline input_line < "/dev/tty"
    4. print "You entered: " input_line
    5. }'

    在此示例中,我们使用 getline 函数从标准输入(通常是终端)读取一行文本。AWK会在执行到 getline 语句时等待用户输入,并将输入的文本存储在变量 input_line 中。然后,我们使用 print 命令将用户输入的文本打印出来。
    运行此命令后,AWK会等待用户输入一行文本,然后将其打印出来。

    注意:Ctrl-D 表示输入结束,而不是终止AWK程序。如果需要终止AWK程序,可以按下 Ctrl-C 组合键。

    getline 的使用方式非常灵活,可以根据需要从不同的输入源中读取数据,并在AWK程序中进行处理。它在处理需要与文件或用户输入交互的情况下非常有用。

    文件操作

    在AWK中,文件操作涉及打开文件、关闭文件、输出到文件以及重定向到文件等一系列操作,这些操作允许你读取和写入文件。以下是AWK中文件操作的详细解释以及示例:

    打开文件

    你可以使用 getline 函数来打开文件以供读取。通常,getline 语句用于从文件中读取数据,并将其存储到变量中。文件名应该放在尖括号 < 后面,如下所示:

    getline line < "filename"

    这会将 "filename" 文件的下一行读取到 line 变量中。

    关闭文件

    在AWK中,不需要显式关闭文件。AWK会自动管理文件的打开和关闭。当使用 getline 读取文件的最后一行后,文件会自动关闭。

    重定向到文件

    要将数据写入文件,可以使用 print 命令将内容输出到文件中。使用 > 符号可以将输出附加到文件。

    • >:将数据输出到文件,并覆盖文件的内容。如果文件不存在,则会创建文件。
    • >>:将数据输出到文件并追加到文件的末尾。如果文件不存在,则会创建文件。

    示例如下:

    • 将数据写入文件:
    print "Hello, World!" > "output.txt"
    • 将数据附加到文件:
    print "More data" >> "output.txt"

    示例

    以下是一个综合示例,演示了如何打开文件、读取文件、输出到文件以及将标准输出重定向到文件。AWK脚本文件myawkscript.awk的内容:

    1. #!/usr/bin/awk -f
    2. # BEGIN 块
    3. BEGIN {
    4. FS = ","
    5. }
    6. # 主处理块
    7. # 从文件 "numbers.txt" 读取数据并处理
    8. {
    9. file = "numbers.txt" # 设置输入文件名
    10. while ((getline line < file) > 0) { # 使用循环读取每一行
    11. print "Read from file:", line # 打印从文件中读取的数据
    12. # 处理数据
    13. split(line, fields, ",") # 使用逗号分隔行数据
    14. if (fields[2] > 10) {
    15. print fields[1], fields[2] > "output.txt" # 输出满足条件的数据到 "output.txt" 文件
    16. }
    17. }
    18. close(file) # 关闭输入文件
    19. close("output.txt") # 关闭输出文件
    20. }
    21. # END 块
    22. # 重定向标准输出到文件
    23. END {
    24. print "Output redirected to output.txt" # 打印输出重定向到 "output.txt" 的消息
    25. }

     假设有一个名为 numbers.txt 的文本文件,包含以下内容:

    1. 5,12,8
    2. 15,30,20
    3. 7,9,11

    AWK脚本文件文件名是 myawkscript.awk,可以使用以下方式运行它:

    awk -f myawkscript.awk

    这将使用 awk 解释器来执行 myawkscript.awk 文件中的AWK代码。实现如下所示:

    补充

    getline 函数

    getline 函数在AWK中用于从文件中读取数据,并将其存储在指定的变量中。每次 getline 被调用,它都会自动转到文件的下一行,直到文件的末尾为止。它返回一个整数值,用于指示读取操作的结果:

    • 如果成功读取了一行数据,则 getline 返回 1。
    • 如果达到文件末尾或发生读取错误,则 getline 返回 0。
    • 如果发生致命错误(例如文件不存在),则 getline 返回 -1。
    split 函数

    split(line, fields, ",") 是AWK中的一个函数调用,用于将字符串 line 按照指定的分隔符(在这里是逗号 ,)拆分成多个部分,并将这些部分存储在数组 fields 中。每个拆分后的部分将成为数组的一个元素,你可以通过数组索引访问这些元素。例如,fields[1] 表示第一个拆分后的部分,fields[2] 表示第二个拆分后的部分,以此类推。

    fields 是一个用于存储拆分结果的数组,你可以自定义数组的名称。在这个例子中,我们使用了 fields 作为数组名称。

    条件语句

    awk '{ if ($2 > 50) print $1, "Pass"; else print $1, "Fail" }' scores.txt

    这将根据第二个字段的值决定学生是否通过。

    scores.txt文件内容:

    循环

    awk '{ for (i=1; i<=NF; i++) print $i }' english.txt

    这将遍历每一行的字段并打印它们。


    实战~一个面试题

    题目:监控 SSH 登录失败并在 /etc/hosts.deny 中添加被封禁 IP 地址

    检查/var/log/secure日志文件,如果有主机用root用户连接服务器的ssh服务失败次数超过了10次,就将这个IP地址写入到/etc/hosts.deny文件,拒绝其访问,如果这个IP已经存在,就无需重复添加到/etc/hosts.deny文件。

    解释:

    1. 脚本首先使用 awk 命令从 /var/log/secure 中提取包含 "Failed" 的行,并将其第11个字段(通常是 IP 地址)提取出来,然后使用 sort 和 uniq -c 统计每个 IP 地址出现的次数,最后将结果写入 sshd.txt 文件中。
    2. 接下来,从 sshd.txt 文件中分离 IP 地址和相应的登录失败次数,并将它们存储在数组 ip_addr 和 ip_num 中。
    3. 然后,遍历每个 IP 地址,检查它是否已经存在于 /etc/hosts.deny 文件中。如果存在,则输出该信息;如果不存在且登录失败次数超过10次,则将该 IP 地址添加到 /etc/hosts.deny 中。
    4. 最后,脚本进入一个无限循环,每5秒钟检查一次 /var/log/secure 中的登录失败日志,如果某个 IP 地址登录失败次数超过10次,则将其添加到 /etc/hosts.deny 中。

    解答:

    具体实现,以下脚本文件的内容:

    1. #!/bin/bash
    2. # 上面一行指定了要使用的解释器为 Bash。
    3. # 提取/var/log/secure中包含"Failed"的行,并统计IP地址出现次数,保存到sshd.txt
    4. # sort命令将 awk 输出的数据进行排序,以便相同的 IP 地址会被聚合在一起
    5. # uniq -c命令用于去除连续重复的行,并在行首显示出现的数目。
    6. awk '$0 ~ /Failed/ {print $11}' /var/log/secure | sort | uniq -c | awk '{print $1, $2}' > sshd.txt
    7. # 从sshd.txt中分离IP地址和登录失败次数,存储到数组
    8. # $(...)是命令替换的语法。它将执行命令并将其输出作为字符串返回,作为数组ip_addr的元素
    9. ip_addr=($(awk '{print $2}' sshd.txt))  
    10. ip_num=($(awk '{print $1}' sshd.txt))
    11. # 遍历IP地址和登录失败次数
    12. for i in ${!ip_addr[@]}; do
    13. ip=${ip_addr[i]} 
    14. num=${ip_num[i]}
    15. # 检查是否已经存在于/etc/hosts.deny中
    16. # egrep 命令用来在 /etc/hosts.deny 文件中查找是否已经存在指定的 IP 地址 ${ip}
    17. # &>/dev/null 是Linux/Unix系统中用于重定向命令输出的一种常见方式。将命令的输出和错误都重定向到 /dev/null。
    18. # /dev/null 是一个特殊的设备文件,它被用作"黑洞"。任何写入 /dev/null 的内容都会被丢弃,而且不会显示在终端上。这常常用于丢弃不需要的输出。
    19. if egrep "${ip}" /etc/hosts.deny &>/dev/null; then
    20. echo "${ip} already exists in /etc/hosts.deny"
    21. else
    22. echo "################################"
    23. # 如果登录失败次数大于10,则添加到/etc/hosts.deny中
    24. if (($num > 10)); then
    25. echo "sshd:${ip}" >> /etc/hosts.deny
    26. echo "Added ${ip} to /etc/hosts.deny"
    27. fi
    28. echo "${ip} have access ${num} times"
    29. fi
    30. done
    31. # 无限循环,每5秒检查/var/log/secure中的登录失败日志
    32. # egrep '^Failed':过滤出登录状态为 "Failed" 的行
    33. # sort:对结果进行排序,以便相同的 IP 地址被聚合在一起
    34. # uniq -c:使用 uniq 命令计数每个唯一 IP 地址出现的次数
    35. while true; do
    36. IP_list=($(awk '{print $6,$11}' /var/log/secure | egrep '^Failed' | sort | uniq -c | awk '{if($1 > 10) print $3}'))
    37. # 遍历新的登录失败IP地址
    38. for ip in ${IP_list[@]}; do
    39. # 检查是否已经存在于/etc/hosts.deny中
    40. if egrep "${ip}" /etc/hosts.deny &>/dev/null; then
    41. : # 什么都不做
    42. else
    43. # 否则,将IP地址添加到/etc/hosts.deny中
    44. echo "sshd:${ip}:deny" >> /etc/hosts.deny
    45. echo "Added ${ip} to /etc/hosts.deny"
    46. fi
    47. done
    48. # 在每次循环结束后,脚本会等待 5 秒钟,然后再次开始检查日志文件
    49. sleep 5
    50. done


    总结

    AWK命令是Linux中一个功能强大的文本处理工具,它可以处理文本文件的各种任务,包括搜索、提取、转换和报告生成。通过了解基本用法、字段和分隔符、内置变量以及高级功能,您可以更高效地处理文本数据。无论您是在日常系统管理中还是在数据分析中,AWK都是一个有用的工具,值得掌握。希望本文能够帮助您更深入地了解AWK命令并提高您的文本处理技能。

  • 相关阅读:
    基于Springboot实现学生毕业离校系统项目【项目源码+论文说明】分享
    搭建本地MQTT服务器
    【English】十大词性之数词
    资深Java工程师蚂蚁金服三面详解,看完你也可以拿offer
    NoSQL--3.MongoDB配置(Linux版)
    多线程(【多线程案例】单例模式+阻塞式队列+定时器+线程池)
    js for循环案例
    混沌反馈共享和群体协同效应的蝴蝶优化算法—附代码
    【蓝桥】通关
    如何实现使用原生的Java API实现代理模式_java培训
  • 原文地址:https://blog.csdn.net/weixin_60461563/article/details/133421187