From c88ae9137771fbebc94d61db87cc77f97b2deb19 Mon Sep 17 00:00:00 2001 From: wxin <15253413025@163.com> Date: Sun, 30 Mar 2025 13:46:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20awk/awk-=E5=9F=BA=E7=A1=80?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- awk/awk-基础.md | 1054 +++++++++++++++++++++++------------------------ 1 file changed, 527 insertions(+), 527 deletions(-) diff --git a/awk/awk-基础.md b/awk/awk-基础.md index ff74721..0e30f50 100644 --- a/awk/awk-基础.md +++ b/awk/awk-基础.md @@ -1,527 +1,527 @@ -

Shell awk

- ------- - -## 一:AWK - -### 1. awk 介绍 - -​ `awk`是一种编程语言,用于在`linux/unix`下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是`linux/unix`下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。 - -​ `awk`的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。`awk`分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是`Alfred Aho`、`Brian Kernighan`、`Peter Weinberger`。`gawk`是`awk`的`GNU`版本,它提供了`Bell`实验室和`GNU`的一些扩展。 - -### 2. 语法格式 - -```shell -awk [-F field-separator] 'commands' input-file(s) -awk [options] 'commands' var=value file(s) -``` - -- `commands`是真正 AWK 命令,[-F域分隔符]是可选的。 `input-file(s)`是待处理的文件。 -- `awk`中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名`-F`域分隔符的情况下,默认的域分隔符是空格。 - -```shell -awk -f awk-script-file input-file(s) -awk [options] -f scriptfile var=value file(s) -``` - -- `-f`选项加载`awk-script-file`中的`awk`脚本,`input-file(s)`是待处理的文件。 -- 将所有的`awk`命令插入一个脚本文件,使用`awk`命令解释器作为脚本的首行,相当于`shell`脚本首行的:`#!/bin/sh`可以换成:`#!/bin/awk`。通过执行脚本来调用。 - -### 3. 工作原理 - -`awk`工作流程可分为三个部分: - -- 读输入文件之前执行的代码段(由`BEGIN`关键字标识)。 -- 主循环执行输入文件的代码段。 -- 读输入文件之后的代码段(由`END`关键字标识)。 - -**`awk`命令结构** - -```shell -awk 'BEGIN{ commands } pattern{ commands } END{ commands }' -``` - -下图是`awk`的工作流程 - -![](../accents\images-202503300003.png) - -- 通过关键字`BEGIN`执行`BEGIN`块的内容,即 BEGIN 后花括号 **{}** 的内容。 -- 完成`BEGIN`块的执行,开始执行`body`块。 -- 读入有 **\n** 换行符分割的记录。 -- 将记录按指定的域分隔符划分域,填充域,**$0** 则表示所有域(即一行内容),**$1** 表示第一个域,**$n** 表示第 n 个域。 -- 依次执行各`BODY`块,`pattern`部分匹配该行内容成功后,才会执行`awk-commands`的内容。 -- 循环读取并执行各行直到文件结束,完成`body`块执行。 -- 开始`END`块执行,`END`块可以输出最终结果。 - -**开始块** - -开始块的语法格式如下: - -```shell -BEGIN {awk-commands} -``` - -​ 开始块就是在程序启动的时候执行的代码部分,并且它在整个过程中只执行一次。 - -​ 一般情况下,我们可以在开始块中初始化一些变量。 - -​ `BEGIN`是`AWK`的关键字,因此它必须是大写的。 - -**注意:** - -​ 开始块部分是可选的,你的程序可以没有开始块部分。 - -**主体块(BODY)** - -主体部分的语法格式如下: - -```shell -/pattern/ {awk-commands} -``` - -​ 对于每一个输入的行都会执行一次主体部分的命令。 - -​ 默认情况下,对于输入的每一行,`AWK`都会执行命令。但是,我们可以将其限定在指定的模式中。 - -**注意**: - -​ 在主体块部分没有关键字存在。 - -**结束块(END)** - -结束块的语法格式如下: - -```shell -END {awk-commands} -``` - -​ 结束块是在程序结束时执行的代码。`END`也是`AWK`的关键字,它也必须大写。 与开始块相似,结束块也是可选的。 - -**示例:** - -​ 先创建一个名为 marks.txt 的文件。其中包括序列号、学生名字、课程名称与所得分数。 - -```shell -1) 张三 语文 80 -2) 李四 数学 90 -3) 王五 英语 87 -``` - -​ 接下来,我们将使用 AWK 脚本来显示输出文件中的内容,同时输出表头信息。 - -```bash -awk 'BEGIN{printf "序号\t名字\t课程\t分数\n"} {print}' marks.txt -``` - -​ 执行以上命令,输出结果如下: - -```shell -序号 名字 课程 分数 -1) 张三 语文 80 -2) 李四 数学 90 -3) 王五 英语 87 -``` - -​ 程序开始执行时,`AWK`在开始块中输出表头信息。在主体块中,`AWK`每读入一行就将读入的内容输出至标准输出流中,一直到整个文件被全部读入为止。 - -### 4. awk 选项 - -```shell --F fs or --field-separator fs -# 指定输入文件折分隔符,fs 是一个字符串或者是一个正则表达式,如 -F:。 - --v var=value or --asign var=value -# 赋值一个用户定义变量。 - --f scripfile or --file scriptfile -# 从脚本文件中读取 awk 命令。 - --mf nnn and -mr nnn -# 对 nnn 值设置内在限制,-mf 选项限制分配给 nnn 的最大块数目;-mr 选项限制记录的最大数目。这两个功能是 Bell 实验室版 awk 的扩展功能,在标准 awk 中不适用。 - --W compact or --compat, -W traditional or --traditional -# 在兼容模式下运行 awk。所以 gawk 的行为和标准的 awk 完全一样,所有的 awk 扩展都被忽略。 - --W copyleft or --copyleft, -W copyright or --copyright -# 打印简短的版权信息。 - --W help or --help, -W usage or --usage -# 打印全部 awk 选项和每个选项的简短说明。 - --W lint or --lint -# 打印不能向传统 unix 平台移植的结构的警告。 - --W lint-old or --lint-old -# 打印关于不能向传统 unix 平台移植的结构的警告。 - --W posix -# 打开兼容模式。但有以下限制,不识别:/x、函数关键字、func、换码序列以及当 fs 是一个空格时,将新行作为一个域分隔符;操作符 ** 和 **= 不能代替 ^ 和 ^=;fflush无效。 - --W re-interval or --re-inerval -# 允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。 - --W source program-text or --source program-text -# 使用 program-text 作为源代码,可与 -f 命令混用。 - --W version or --version -# 打印 bug 报告信息的版本。 -``` - -### 5. 格式化打印 - -- print 和 printf 都是打印输出的,不过两者用法和显示上有些不同而已。 - -```shell -print 格式:print item1,item2, ... -printf格式:printf “FORMAT ”,item1,item2, ... -``` - -- 逗号为分隔符时,显示的是空格; -- 分隔符分隔的字段(域)标记称为域标识,用$0,$1,$2,...,$n表示,其中$0 为所有域,$1就是表示第一个字段(域),以此类推; - -- 输出的各item可以字符串,也可以是数值,当前记录的字段,变量或awk 的表达式等; - -- 如果省略了item ,相当于print $0 - -- 对于printf来说,其中格式化字符串包括两部分内容: 一部分是正常字符,这些字符将按原样输出; 另一部分是格式化规定字符, 以 **%** 开始, 后跟一个或几个规定字符,用来确定输出内容格式,必须指定 FORMAT,即必须指出后面每个itemsN 的输出格式,**printf** 时默认是不会换行的,而 **print** 函数默认会在每行后面加上 **\n** 换行符 - -| **格式符** | **说明** | -| :--------: | :----------------------------------------------------------: | -| %d | 十进制有符号整数 | -| %u | 十进制无符号整数 | -| %f | 浮点数 | -| %s | 字符串 | -| %c | 单个字符 | -| %p | 指针的值 | -| %e | 指数形式的浮点数 | -| %x | %X 无符号以十六进制表示的整数 | -| %o | 无符号以八进制表示的整数 | -| %g | 自动选择合适的表示法 | -| %% | 显示%自身 | -| #[.#] | 第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f | -| - | 左对齐(默认右对齐);%-15s,就是以左对齐方式显示15个字符长度 | -| + | 显示数值的正负符号 %+d | - -**示例:** - -```bash -# print 函数 -[root@wxin ~]# awk '{print "hello,awk"}' -[root@wxin ~]# awk –F: '{print}' /etc/passwd -[root@wxin ~]# awk –F: ‘{print “wang”}’ /etc/passwd -[root@wxin ~]# awk –F: ‘{print $1}’ /etc/passwd -[root@wxin ~]# awk –F: ‘{print $0}’ /etc/passwd -[root@wxin ~]# awk –F: ‘{print $1”\t”$3}’ /etc/passwd -[root@wxin ~]# date |awk '{print "Month: " $2 "\nYear: " $NF}' -[root@wxin ~]# awk -F: '{print "username is: " $1 "\t uid is: " $3}' /etc/passwd -[root@wxin ~]# awk -F: '{print "\tusername and uid: " $1,$3 "!"}' /etc/passwd - -# printf函数 -[root@wxin ~]# tail –3 /etc/fstab |awk ‘{print $2,$4}’ -[root@wxin ~]# awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd -[root@wxin ~]# awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd -[root@wxin ~]# awk -F: ‘{printf "%s",$1}’ /etc/passwd -[root@wxin ~]# awk -F: ‘{printf "%s\n",$1}’ /etc/passwd -[root@wxin ~]# awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd -[root@wxin ~]# awk -F: ‘{printf "Username: %s\n",$1}’ /etc/passwd -[root@wxin ~]# awk -F: ‘{printf “Username: %s,UID:%d\n",$1,$3}’ /etc/passwd -[root@wxin ~]# awk -F: ‘{printf "Username: %15s,UID:%d\n",$1,$3}’ /etc/passwd -[root@wxin ~]# awk -F: ‘{printf "Username: %-15s,UID:%d\n",$1,$3}’ /etc/passwd -[root@wxin ~]# lsmod | awk -v FS=" " 'BEGIN{printf "%s %26s %10s\n","Module","Size","Used by"}{printf "%-20s %13d %5s %s\n",$1,$2,$3,$4}' /proc/modules -``` - -### 6. awk 运算符 - -| 运算符 | 描述 | -| :---------------------: | :------------------------------: | -| = += -= *= /= %= ^= **= | 赋值 | -| ?: | C条件表达式 | -| \|\| | 逻辑或 | -| && | 逻辑与 | -| ~ 和 !~ | 匹配正则表达式和不匹配正则表达式 | -| < <= > >= != == | 关系运算符 | -| 空格 | 连接 | -| + - | 加,减 | -| * / % | 乘,除与求余 | -| + - ! | 一元加,减和逻辑非 | -| ^ *** | 求幂 | -| ++ -- | 增加或减少,作为前缀或后缀 | -| $ | 字段引用 | -| in | 数组成员 | - -**示例:** - -过滤第一列大于2的行 - -```bash -[root@wxin ~]# awk '$1>2' log.txt -3 Are you like awk -This's a test -10 There are orange,apple,mongo -``` - -过滤第一列等于2的行 - -```bash -[root@wxin ~]# awk '$1==2 {print $1,$3}' log.txt -2 is -``` - -过滤第一列大于2并且第二列等于'Are'的行 - -```bash -[root@wxin ~]# awk '$1>2 && $2=="Are" {print $1,$2,$3}' log.txt -3 Are you -``` - -过滤练习 - -```bash -[root@wxin ~]# awk -F: '$3 == 0' /etc/passwd -[root@wxin ~]# awk -F: '$3 < 10' /etc/passwd -[root@wxin ~]# awk -F: '$NF == "/bin/bash"' /etc/passwd -[root@wxin ~]# awk -F: '$1 == "alice"' /etc/passwd -[root@wxin ~]# awk -F: '$1 ~ /alic/ ' /etc/passwd -[root@wxin ~]# awk -F: '$1 !~ /alic/ ' /etc/passwd -[root@wxin ~]# df -P | grep '/' |awk '$4 > 25000' -``` - -### 7. awk 变量 - -**`awk`内置变量** - -| 变量 | 描述 | -| :---------: | :--------------------------------------------------------: | -| $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) | - -**示例:** - -```bash -[root@wxin ~]# awk -F: '{print $0}' /etc/passwd # $0 -[root@wxin ~]# awk -F: '{print NR, $0}' /etc/passwd /etc/hosts # NR -[root@wxin ~]# awk -F: '{print FNR, $0}' /etc/passwd /etc/hosts # FNR -[root@wxin ~]# awk -F: '{print $0,NF}' /etc/passwd # NF -[root@wxin ~]# awk -F: '/alice/{print $1, $3}' /etc/passwd # FS -[root@wxin ~]# awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd -[root@wxin ~]# awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd -[root@wxin ~]# awk -F: '/alice/{print $1,$2,$3,$4}' /etc/passwd # OFS -[root@wxin ~]# awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2,$3,$4}' passwd -[root@wxin ~]# awk -F: 'BEGIN{RS=" "} {print $0}' a.txt # RS -[root@wxin ~]# awk -F: 'BEGIN{ORS=""} {print $0}' passwd # ORS -``` - -- 字段分隔符: FS OFS 默认空格或制表符 -- 记录分隔符: RS ORS 默认换行符 - -```bash -# ORS 默认输出一条记录应该回车,加了一个空格 -[root@wxin ~]# awk 'BEGIN{ORS=" "} {print $0}' /etc/passwd # 将文件每一行合并为一行 - -[root@wxin ~]# head -1 /etc/passwd > passwd1 -[root@wxin ~]# cat passwd1 -root:x:0:0:root:/root:/bin/bash -[root@wxin ~]# awk 'BEGIN{RS=":"} {print $0}' passwd1 -root -x -0 -0 -root -/root -/bin/bash - -[root@wxin ~]# awk 'BEGIN{RS=":"} {print $0}' passwd1 |grep -v '^$' > passwd2 - -# 输出顺序号 NR, 匹配文本行号 -[root@wxin ~]# awk '{print NR,FNR,$1,$2,$3}' /etc/passwd -1 1 root:x:0:0:root:/root:/bin/bash -2 2 bin:x:1:1:bin:/bin:/sbin/nologin -3 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin -4 4 adm:x:3:4:adm:/var/adm:/sbin/nologin -5 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin - -# 指定输出分割符 -[root@wxin ~]# awk '{print $1,$2,$5}' OFS=" $ " /etc/passwd -root:x:0:0:root:/root:/bin/bash $ $ -bin:x:1:1:bin:/bin:/sbin/nologin $ $ -daemon:x:2:2:daemon:/sbin:/sbin/nologin $ $ -adm:x:3:4:adm:/var/adm:/sbin/nologin $ $ -lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin $ $ -``` - -**`awk`自定义变量(区分大小写)** - -```shell -在'{...}'前,需要用-v var=value:awk -v var=value '{...}' -在program 中直接定义:awk '{var=vlue}' -``` - -**`awk`使用外部变量** - -在双引号的情况下使用 - -```bash -[root@wxin ~]# var="bash" -[root@wxin ~]# echo "unix script" |awk "gsub(/unix/,\"$var\")" -bash script -``` - -在单引号的情况下使用 - -```bash -[root@wxin ~]# var="bash" -[root@wxin ~]# echo "unix script" |awk 'gsub(/unix/,"'"$var"'")' -bash script - -[root@wxin ~]# df -h -Filesystem Size Used Avail Use% Mounted on -/dev/mapper/cl-root 2.8T 246G 2.5T 9% / -tmpfs 24G 20K 24G 1% /dev/shm -/dev/sda2 1014M 194M 821M 20% /boot - -[root@wxin ~]# df -h |awk '{ if(int($5)>5){print $6":"$5} }' -/:9% -/boot:20% - -[root@wxin ~]# i=10 -[root@wxin ~]# df -h |awk '{ if(int($5)>'''$i'''){print $6":"$5} }' -/boot:20% -``` - -AWK 参数-v(建议) - -```bash -[root@wxin ~]# echo "unix script" |awk -v var="bash" 'gsub(/unix/,var)' -bash script - -[root@wxin ~]# awk -v user=root -F: '$1 == user' /etc/passwd -root:x:0:0:root:/root:/bin/bash -``` - -### 8. awk 脚本 - -**`awk`脚本定义格式** - -```shell -格式1: -BEGIN{} pattern{} END{} - -格式2: -#!/bin/awk -f -#add 'x' right -BEGIN{} pattern{} END{} -``` - -关于awk 脚本,需要注意两个关键词BEGIN和END。 - -- BEGIN{ 这里面放的是执行前的语句 } -- END {这里面放的是处理完所有的行后要执行的语句 } -- {这里面放的是处理每一行时要执行的语句} - -- 格式1假设为`f1.awk`文件,格式2假设为`f2.awk`文件 - -```shell -awk [-v var=value] f1.awk [file] -f2.awk [-v var=value] [var1=value1] [file] -``` - -- awk [-v var=value] f1.awk [file],把处理阶段放到一个文件而已,展开后就是普通的`awk`语句。 -- f2.awk [-v var=value] [var1=value1] [file] 中 [-v var=value] 是在`BEGIN`之前设置的变量值,[var1=value1]是在BEGIN过程之后进行的,也就是说直到首行输入完成后,这个变量才可用。 - -**示例:** - -示例一: - -创建一个文件(学生成绩表) - -```bash -[root@wxin ~]# cat score.txt -Marry 2143 78 84 77 -Jack 2321 66 78 45 -Tom 2122 48 77 71 -Mike 2537 87 97 95 -Bob 2415 40 57 62 -``` - -定义 awk 脚本 - -```bash -[root@wxin ~]# cat cal.awk -#!/bin/awk -f -#运行前 -BEGIN { - math = 0 - english = 0 - computer = 0 - - printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n" - printf "---------------------------------------------\n" -} -#运行中 -{ - math+=$3 - english+=$4 - computer+=$5 - printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5 -} -#运行后 -END { - printf "---------------------------------------------\n" - printf " TOTAL:%10d %8d %8d \n", math, english, computer - printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR -} - -[root@wxin ~]# awk -f cal.awk score.txt -NAME NO. MATH ENGLISH COMPUTER TOTAL ---------------------------------------------- -Marry 2143 78 84 77 239 -Jack 2321 66 78 45 189 -Tom 2122 48 77 71 196 -Mike 2537 87 97 95 279 -Bob 2415 40 57 62 159 ---------------------------------------------- - TOTAL: 319 393 350 -AVERAGE: 63.80 78.60 70.00 -``` - -示例二: - -```bash -[root@wxin ~]# vim f1.awk - {if($3>=1000)print $1,$3} -awk -F: -f f1.awk /etc/passwd - -[root@wxin ~]# vim f2.awk - #!/bin/awk –f - # this is a awk script - {if($3>=1000)print $1,$3} - # chmod +x f2.awk -f2.awk –F: /etc/passwd - -[root@wxin ~]# vim test.awk - #!/bin/awk –f - {if($3 >=min && $3<=max)print $1,$3} - #chmod +x test.awk -test.awk -F: min=100 max=200 /etc/passwd -``` - +

Shell awk

+ +------ + +## 一:AWK + +### 1. awk 介绍 + +​ `awk`是一种编程语言,用于在`linux/unix`下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是`linux/unix`下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。 + +​ `awk`的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。`awk`分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是`Alfred Aho`、`Brian Kernighan`、`Peter Weinberger`。`gawk`是`awk`的`GNU`版本,它提供了`Bell`实验室和`GNU`的一些扩展。 + +### 2. 语法格式 + +```shell +awk [-F field-separator] 'commands' input-file(s) +awk [options] 'commands' var=value file(s) +``` + +- `commands`是真正 AWK 命令,[-F域分隔符]是可选的。 `input-file(s)`是待处理的文件。 +- `awk`中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名`-F`域分隔符的情况下,默认的域分隔符是空格。 + +```shell +awk -f awk-script-file input-file(s) +awk [options] -f scriptfile var=value file(s) +``` + +- `-f`选项加载`awk-script-file`中的`awk`脚本,`input-file(s)`是待处理的文件。 +- 将所有的`awk`命令插入一个脚本文件,使用`awk`命令解释器作为脚本的首行,相当于`shell`脚本首行的:`#!/bin/sh`可以换成:`#!/bin/awk`。通过执行脚本来调用。 + +### 3. 工作原理 + +`awk`工作流程可分为三个部分: + +- 读输入文件之前执行的代码段(由`BEGIN`关键字标识)。 +- 主循环执行输入文件的代码段。 +- 读输入文件之后的代码段(由`END`关键字标识)。 + +**`awk`命令结构** + +```shell +awk 'BEGIN{ commands } pattern{ commands } END{ commands }' +``` + +下图是`awk`的工作流程 + +![](http://182.92.143.66:40072/directlink/img/shell/images-202503300003.png) + +- 通过关键字`BEGIN`执行`BEGIN`块的内容,即 BEGIN 后花括号 **{}** 的内容。 +- 完成`BEGIN`块的执行,开始执行`body`块。 +- 读入有 **\n** 换行符分割的记录。 +- 将记录按指定的域分隔符划分域,填充域,**$0** 则表示所有域(即一行内容),**$1** 表示第一个域,**$n** 表示第 n 个域。 +- 依次执行各`BODY`块,`pattern`部分匹配该行内容成功后,才会执行`awk-commands`的内容。 +- 循环读取并执行各行直到文件结束,完成`body`块执行。 +- 开始`END`块执行,`END`块可以输出最终结果。 + +**开始块** + +开始块的语法格式如下: + +```shell +BEGIN {awk-commands} +``` + +​ 开始块就是在程序启动的时候执行的代码部分,并且它在整个过程中只执行一次。 + +​ 一般情况下,我们可以在开始块中初始化一些变量。 + +​ `BEGIN`是`AWK`的关键字,因此它必须是大写的。 + +**注意:** + +​ 开始块部分是可选的,你的程序可以没有开始块部分。 + +**主体块(BODY)** + +主体部分的语法格式如下: + +```shell +/pattern/ {awk-commands} +``` + +​ 对于每一个输入的行都会执行一次主体部分的命令。 + +​ 默认情况下,对于输入的每一行,`AWK`都会执行命令。但是,我们可以将其限定在指定的模式中。 + +**注意**: + +​ 在主体块部分没有关键字存在。 + +**结束块(END)** + +结束块的语法格式如下: + +```shell +END {awk-commands} +``` + +​ 结束块是在程序结束时执行的代码。`END`也是`AWK`的关键字,它也必须大写。 与开始块相似,结束块也是可选的。 + +**示例:** + +​ 先创建一个名为 marks.txt 的文件。其中包括序列号、学生名字、课程名称与所得分数。 + +```shell +1) 张三 语文 80 +2) 李四 数学 90 +3) 王五 英语 87 +``` + +​ 接下来,我们将使用 AWK 脚本来显示输出文件中的内容,同时输出表头信息。 + +```bash +awk 'BEGIN{printf "序号\t名字\t课程\t分数\n"} {print}' marks.txt +``` + +​ 执行以上命令,输出结果如下: + +```shell +序号 名字 课程 分数 +1) 张三 语文 80 +2) 李四 数学 90 +3) 王五 英语 87 +``` + +​ 程序开始执行时,`AWK`在开始块中输出表头信息。在主体块中,`AWK`每读入一行就将读入的内容输出至标准输出流中,一直到整个文件被全部读入为止。 + +### 4. awk 选项 + +```shell +-F fs or --field-separator fs +# 指定输入文件折分隔符,fs 是一个字符串或者是一个正则表达式,如 -F:。 + +-v var=value or --asign var=value +# 赋值一个用户定义变量。 + +-f scripfile or --file scriptfile +# 从脚本文件中读取 awk 命令。 + +-mf nnn and -mr nnn +# 对 nnn 值设置内在限制,-mf 选项限制分配给 nnn 的最大块数目;-mr 选项限制记录的最大数目。这两个功能是 Bell 实验室版 awk 的扩展功能,在标准 awk 中不适用。 + +-W compact or --compat, -W traditional or --traditional +# 在兼容模式下运行 awk。所以 gawk 的行为和标准的 awk 完全一样,所有的 awk 扩展都被忽略。 + +-W copyleft or --copyleft, -W copyright or --copyright +# 打印简短的版权信息。 + +-W help or --help, -W usage or --usage +# 打印全部 awk 选项和每个选项的简短说明。 + +-W lint or --lint +# 打印不能向传统 unix 平台移植的结构的警告。 + +-W lint-old or --lint-old +# 打印关于不能向传统 unix 平台移植的结构的警告。 + +-W posix +# 打开兼容模式。但有以下限制,不识别:/x、函数关键字、func、换码序列以及当 fs 是一个空格时,将新行作为一个域分隔符;操作符 ** 和 **= 不能代替 ^ 和 ^=;fflush无效。 + +-W re-interval or --re-inerval +# 允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。 + +-W source program-text or --source program-text +# 使用 program-text 作为源代码,可与 -f 命令混用。 + +-W version or --version +# 打印 bug 报告信息的版本。 +``` + +### 5. 格式化打印 + +- print 和 printf 都是打印输出的,不过两者用法和显示上有些不同而已。 + +```shell +print 格式:print item1,item2, ... +printf格式:printf “FORMAT ”,item1,item2, ... +``` + +- 逗号为分隔符时,显示的是空格; +- 分隔符分隔的字段(域)标记称为域标识,用$0,$1,$2,...,$n表示,其中$0 为所有域,$1就是表示第一个字段(域),以此类推; + +- 输出的各item可以字符串,也可以是数值,当前记录的字段,变量或awk 的表达式等; + +- 如果省略了item ,相当于print $0 + +- 对于printf来说,其中格式化字符串包括两部分内容: 一部分是正常字符,这些字符将按原样输出; 另一部分是格式化规定字符, 以 **%** 开始, 后跟一个或几个规定字符,用来确定输出内容格式,必须指定 FORMAT,即必须指出后面每个itemsN 的输出格式,**printf** 时默认是不会换行的,而 **print** 函数默认会在每行后面加上 **\n** 换行符 + +| **格式符** | **说明** | +| :--------: | :----------------------------------------------------------: | +| %d | 十进制有符号整数 | +| %u | 十进制无符号整数 | +| %f | 浮点数 | +| %s | 字符串 | +| %c | 单个字符 | +| %p | 指针的值 | +| %e | 指数形式的浮点数 | +| %x | %X 无符号以十六进制表示的整数 | +| %o | 无符号以八进制表示的整数 | +| %g | 自动选择合适的表示法 | +| %% | 显示%自身 | +| #[.#] | 第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f | +| - | 左对齐(默认右对齐);%-15s,就是以左对齐方式显示15个字符长度 | +| + | 显示数值的正负符号 %+d | + +**示例:** + +```bash +# print 函数 +[root@wxin ~]# awk '{print "hello,awk"}' +[root@wxin ~]# awk –F: '{print}' /etc/passwd +[root@wxin ~]# awk –F: ‘{print “wang”}’ /etc/passwd +[root@wxin ~]# awk –F: ‘{print $1}’ /etc/passwd +[root@wxin ~]# awk –F: ‘{print $0}’ /etc/passwd +[root@wxin ~]# awk –F: ‘{print $1”\t”$3}’ /etc/passwd +[root@wxin ~]# date |awk '{print "Month: " $2 "\nYear: " $NF}' +[root@wxin ~]# awk -F: '{print "username is: " $1 "\t uid is: " $3}' /etc/passwd +[root@wxin ~]# awk -F: '{print "\tusername and uid: " $1,$3 "!"}' /etc/passwd + +# printf函数 +[root@wxin ~]# tail –3 /etc/fstab |awk ‘{print $2,$4}’ +[root@wxin ~]# awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd +[root@wxin ~]# awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd +[root@wxin ~]# awk -F: ‘{printf "%s",$1}’ /etc/passwd +[root@wxin ~]# awk -F: ‘{printf "%s\n",$1}’ /etc/passwd +[root@wxin ~]# awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd +[root@wxin ~]# awk -F: ‘{printf "Username: %s\n",$1}’ /etc/passwd +[root@wxin ~]# awk -F: ‘{printf “Username: %s,UID:%d\n",$1,$3}’ /etc/passwd +[root@wxin ~]# awk -F: ‘{printf "Username: %15s,UID:%d\n",$1,$3}’ /etc/passwd +[root@wxin ~]# awk -F: ‘{printf "Username: %-15s,UID:%d\n",$1,$3}’ /etc/passwd +[root@wxin ~]# lsmod | awk -v FS=" " 'BEGIN{printf "%s %26s %10s\n","Module","Size","Used by"}{printf "%-20s %13d %5s %s\n",$1,$2,$3,$4}' /proc/modules +``` + +### 6. awk 运算符 + +| 运算符 | 描述 | +| :---------------------: | :------------------------------: | +| = += -= *= /= %= ^= **= | 赋值 | +| ?: | C条件表达式 | +| \|\| | 逻辑或 | +| && | 逻辑与 | +| ~ 和 !~ | 匹配正则表达式和不匹配正则表达式 | +| < <= > >= != == | 关系运算符 | +| 空格 | 连接 | +| + - | 加,减 | +| * / % | 乘,除与求余 | +| + - ! | 一元加,减和逻辑非 | +| ^ *** | 求幂 | +| ++ -- | 增加或减少,作为前缀或后缀 | +| $ | 字段引用 | +| in | 数组成员 | + +**示例:** + +过滤第一列大于2的行 + +```bash +[root@wxin ~]# awk '$1>2' log.txt +3 Are you like awk +This's a test +10 There are orange,apple,mongo +``` + +过滤第一列等于2的行 + +```bash +[root@wxin ~]# awk '$1==2 {print $1,$3}' log.txt +2 is +``` + +过滤第一列大于2并且第二列等于'Are'的行 + +```bash +[root@wxin ~]# awk '$1>2 && $2=="Are" {print $1,$2,$3}' log.txt +3 Are you +``` + +过滤练习 + +```bash +[root@wxin ~]# awk -F: '$3 == 0' /etc/passwd +[root@wxin ~]# awk -F: '$3 < 10' /etc/passwd +[root@wxin ~]# awk -F: '$NF == "/bin/bash"' /etc/passwd +[root@wxin ~]# awk -F: '$1 == "alice"' /etc/passwd +[root@wxin ~]# awk -F: '$1 ~ /alic/ ' /etc/passwd +[root@wxin ~]# awk -F: '$1 !~ /alic/ ' /etc/passwd +[root@wxin ~]# df -P | grep '/' |awk '$4 > 25000' +``` + +### 7. awk 变量 + +**`awk`内置变量** + +| 变量 | 描述 | +| :---------: | :--------------------------------------------------------: | +| $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) | + +**示例:** + +```bash +[root@wxin ~]# awk -F: '{print $0}' /etc/passwd # $0 +[root@wxin ~]# awk -F: '{print NR, $0}' /etc/passwd /etc/hosts # NR +[root@wxin ~]# awk -F: '{print FNR, $0}' /etc/passwd /etc/hosts # FNR +[root@wxin ~]# awk -F: '{print $0,NF}' /etc/passwd # NF +[root@wxin ~]# awk -F: '/alice/{print $1, $3}' /etc/passwd # FS +[root@wxin ~]# awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd +[root@wxin ~]# awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd +[root@wxin ~]# awk -F: '/alice/{print $1,$2,$3,$4}' /etc/passwd # OFS +[root@wxin ~]# awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2,$3,$4}' passwd +[root@wxin ~]# awk -F: 'BEGIN{RS=" "} {print $0}' a.txt # RS +[root@wxin ~]# awk -F: 'BEGIN{ORS=""} {print $0}' passwd # ORS +``` + +- 字段分隔符: FS OFS 默认空格或制表符 +- 记录分隔符: RS ORS 默认换行符 + +```bash +# ORS 默认输出一条记录应该回车,加了一个空格 +[root@wxin ~]# awk 'BEGIN{ORS=" "} {print $0}' /etc/passwd # 将文件每一行合并为一行 + +[root@wxin ~]# head -1 /etc/passwd > passwd1 +[root@wxin ~]# cat passwd1 +root:x:0:0:root:/root:/bin/bash +[root@wxin ~]# awk 'BEGIN{RS=":"} {print $0}' passwd1 +root +x +0 +0 +root +/root +/bin/bash + +[root@wxin ~]# awk 'BEGIN{RS=":"} {print $0}' passwd1 |grep -v '^$' > passwd2 + +# 输出顺序号 NR, 匹配文本行号 +[root@wxin ~]# awk '{print NR,FNR,$1,$2,$3}' /etc/passwd +1 1 root:x:0:0:root:/root:/bin/bash +2 2 bin:x:1:1:bin:/bin:/sbin/nologin +3 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin +4 4 adm:x:3:4:adm:/var/adm:/sbin/nologin +5 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin + +# 指定输出分割符 +[root@wxin ~]# awk '{print $1,$2,$5}' OFS=" $ " /etc/passwd +root:x:0:0:root:/root:/bin/bash $ $ +bin:x:1:1:bin:/bin:/sbin/nologin $ $ +daemon:x:2:2:daemon:/sbin:/sbin/nologin $ $ +adm:x:3:4:adm:/var/adm:/sbin/nologin $ $ +lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin $ $ +``` + +**`awk`自定义变量(区分大小写)** + +```shell +在'{...}'前,需要用-v var=value:awk -v var=value '{...}' +在program 中直接定义:awk '{var=vlue}' +``` + +**`awk`使用外部变量** + +在双引号的情况下使用 + +```bash +[root@wxin ~]# var="bash" +[root@wxin ~]# echo "unix script" |awk "gsub(/unix/,\"$var\")" +bash script +``` + +在单引号的情况下使用 + +```bash +[root@wxin ~]# var="bash" +[root@wxin ~]# echo "unix script" |awk 'gsub(/unix/,"'"$var"'")' +bash script + +[root@wxin ~]# df -h +Filesystem Size Used Avail Use% Mounted on +/dev/mapper/cl-root 2.8T 246G 2.5T 9% / +tmpfs 24G 20K 24G 1% /dev/shm +/dev/sda2 1014M 194M 821M 20% /boot + +[root@wxin ~]# df -h |awk '{ if(int($5)>5){print $6":"$5} }' +/:9% +/boot:20% + +[root@wxin ~]# i=10 +[root@wxin ~]# df -h |awk '{ if(int($5)>'''$i'''){print $6":"$5} }' +/boot:20% +``` + +AWK 参数-v(建议) + +```bash +[root@wxin ~]# echo "unix script" |awk -v var="bash" 'gsub(/unix/,var)' +bash script + +[root@wxin ~]# awk -v user=root -F: '$1 == user' /etc/passwd +root:x:0:0:root:/root:/bin/bash +``` + +### 8. awk 脚本 + +**`awk`脚本定义格式** + +```shell +格式1: +BEGIN{} pattern{} END{} + +格式2: +#!/bin/awk -f +#add 'x' right +BEGIN{} pattern{} END{} +``` + +关于awk 脚本,需要注意两个关键词BEGIN和END。 + +- BEGIN{ 这里面放的是执行前的语句 } +- END {这里面放的是处理完所有的行后要执行的语句 } +- {这里面放的是处理每一行时要执行的语句} + +- 格式1假设为`f1.awk`文件,格式2假设为`f2.awk`文件 + +```shell +awk [-v var=value] f1.awk [file] +f2.awk [-v var=value] [var1=value1] [file] +``` + +- awk [-v var=value] f1.awk [file],把处理阶段放到一个文件而已,展开后就是普通的`awk`语句。 +- f2.awk [-v var=value] [var1=value1] [file] 中 [-v var=value] 是在`BEGIN`之前设置的变量值,[var1=value1]是在BEGIN过程之后进行的,也就是说直到首行输入完成后,这个变量才可用。 + +**示例:** + +示例一: + +创建一个文件(学生成绩表) + +```bash +[root@wxin ~]# cat score.txt +Marry 2143 78 84 77 +Jack 2321 66 78 45 +Tom 2122 48 77 71 +Mike 2537 87 97 95 +Bob 2415 40 57 62 +``` + +定义 awk 脚本 + +```bash +[root@wxin ~]# cat cal.awk +#!/bin/awk -f +#运行前 +BEGIN { + math = 0 + english = 0 + computer = 0 + + printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n" + printf "---------------------------------------------\n" +} +#运行中 +{ + math+=$3 + english+=$4 + computer+=$5 + printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5 +} +#运行后 +END { + printf "---------------------------------------------\n" + printf " TOTAL:%10d %8d %8d \n", math, english, computer + printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR +} + +[root@wxin ~]# awk -f cal.awk score.txt +NAME NO. MATH ENGLISH COMPUTER TOTAL +--------------------------------------------- +Marry 2143 78 84 77 239 +Jack 2321 66 78 45 189 +Tom 2122 48 77 71 196 +Mike 2537 87 97 95 279 +Bob 2415 40 57 62 159 +--------------------------------------------- + TOTAL: 319 393 350 +AVERAGE: 63.80 78.60 70.00 +``` + +示例二: + +```bash +[root@wxin ~]# vim f1.awk + {if($3>=1000)print $1,$3} +awk -F: -f f1.awk /etc/passwd + +[root@wxin ~]# vim f2.awk + #!/bin/awk –f + # this is a awk script + {if($3>=1000)print $1,$3} + # chmod +x f2.awk +f2.awk –F: /etc/passwd + +[root@wxin ~]# vim test.awk + #!/bin/awk –f + {if($3 >=min && $3<=max)print $1,$3} + #chmod +x test.awk +test.awk -F: min=100 max=200 /etc/passwd +``` +