diff --git "a/\351\231\266\345\256\227\350\261\252/\347\254\224\350\256\260/20240603-awk\347\224\250\346\263\225.md" "b/\351\231\266\345\256\227\350\261\252/\347\254\224\350\256\260/20240603-awk\347\224\250\346\263\225.md" new file mode 100644 index 0000000000000000000000000000000000000000..1cd48bdc0690562ba2da13119e069e7157eefa84 --- /dev/null +++ "b/\351\231\266\345\256\227\350\261\252/\347\254\224\350\256\260/20240603-awk\347\224\250\346\263\225.md" @@ -0,0 +1,118 @@ +# AWK用法 + +#### awk 是一个强大的文本处理工具,适用于在 Linux 和类 Unix 系统上处理结构化文本数据。它特别适合处理按列组织的文本文件 + +### 基本语法 + +```JS +awk 'pattern { action }' file +``` + +- pattern:可选,指定文本行的匹配模式。 +- action:对匹配的文本行执行的操作。 +- file:要处理的文件。 + +## 选项 + +- F:指定输入字段分隔符。 +- v:定义变量。 +- f:从脚本文件读取 awk 程序 + +### 内置变量 + +- sNF:当前记录的字段数。 +- sNR:当前记录的行号。 +- sFS:输入字段分隔符。 +- sOFS:输出字段分隔符。 +- sRS:输入记录分隔符。 +- sORS:输出记录分隔符。 +- s$0:当前记录(整行)。 +- s$n:当前记录的第 n 个字段。 + +### 打印文件内容 + +```js +awk '{ print }' example.txt +``` + +### 打印特定字段 + +##### 假设 example.txt 内容如下 + +```sh +Alice 30 +Bob 25 +Charlie 35 + +``` + +打印第一列(名字): + +```js +awk '{ print $1 }' example.txt +``` + +### 条件匹配 + +##### 打印年龄大于 30 的行 + +```sh +awk '$2 > 30 { print }' example.txt +``` + +##### 打印名字为 "Bob" 的行 + +```js +awk '$1 == "Bob" { print }' example.tx +``` + +### 使用字段分隔符 + +```sh +awk -F, '{ print $1, $2 }' data.csv +``` + +##### 打印每行的字段数和内容 + +```sh +awk '{ print NF, $0 }' example.txt +``` + +## 使用操作符 + +- +:加 +- -:减 +- *:乘 +- /:除 +- %:取模 +- ^:幂 + +##### 计算每行第二列的平方 + +```JS +awk '{ print $2^2 }' example.txt +``` + +## 开始语句包和结束语句包 + +### 开始语句(BEGIN) + +##### BEGIN 语句块在 awk 开始处理输入文件之前执行。它通常用于初始化变量、设置字段分隔符和其他预处理任务 + +```js +awk 'BEGIN { actions }' file +``` + +### 结束语句块(END) + +##### END 语句块在 awk 完成对所有输入文件的处理后执行。它通常用于打印总结信息或执行清理工作 + +```js +awk '{ actions } END { actions }' file +``` + +#### 开始语句、中间、结束语句 + +```js +awk 'BEGIN {print "姓名"} {sum += $3} {print} END {print "思政成绩", sum}' fenshu.txt +``` diff --git "a/\351\231\266\345\256\227\350\261\252/\347\273\203\344\271\240/02 awk\345\221\275\344\273\244\347\232\204\347\224\250\346\263\225.md" "b/\351\231\266\345\256\227\350\261\252/\347\273\203\344\271\240/02 awk\345\221\275\344\273\244\347\232\204\347\224\250\346\263\225.md" new file mode 100644 index 0000000000000000000000000000000000000000..b6f057b294686edc9c6f3ad12d7972e44ca73fbb --- /dev/null +++ "b/\351\231\266\345\256\227\350\261\252/\347\273\203\344\271\240/02 awk\345\221\275\344\273\244\347\232\204\347\224\250\346\263\225.md" @@ -0,0 +1,683 @@ +# Awk 简介及应用 + +## 1. 介绍 + +Awk 是由 Aho、Weinberger 和 Kernighan 三位科学家开发的,特别擅长文本处理的 Linux 工具,是 Linux 下最常用的工具之一。Awk 也是一种编程语言,在编程语言排行榜上也能排上号。 + +awk、grep、sed是linux操作文本的三大利器,合称*文本三剑客* + +假设已有student_scores.csv 文件(后面的演示都基于这个文件),内容如下 + +``` +姓名,数学,语文,科学,班级 +张伟,85,92,88,1班 +李娜,78.5,81,79.5,2班 +王强,95,89,94.5,3班 +赵敏,90,72.5,70,2班 +刘洋,88,94,90,1班 +陈刚,74,85,80,1班 +杨梅,91,87,89,3班 +黄磊,55.5,90,86,3班 +周红,76,78,80,2班 +王彬,90,93.5,92,1班 +大王,85,86,45.5,2班 +小王,74,56,88,3班 +``` + +## 2. Awk 命令的结构 + +一般情况下,`awk` 命令的基本结构如下: + +```sh +awk [选项] '脚本' 文件 +# 最简单的一个脚本,打印全文 +awk '{ print }' student_scores.csv +``` + +这里的 `[选项]` 是可选的,它们在 `awk` 命令中通常出现在脚本和文件名之前。 + +AWK 脚本由一个或多个语句块组成,每个语句块可以是以下三种之一: + +1. 开始语句块:在程序开始时执行 `BEGIN { print "start" }` +2. 通用语句块:逐行处理文件内容 `pattern { commands }`, +3. 结束语句块:在程序结束时执行 `END { print "end" }` + +示例: +```sh +awk 'BEGIN { print "开始" } pattern { commands } END { print "结束" }' file +# 头尾各加一行 +awk 'BEGIN { print "我是开头"} { print } END { print "我是结尾" } ' student_scores.csv +``` + +可以通过管道获取数据: +```sh +cat file | awk 'BEGIN { print "start" } pattern { commands } END { print "end" }' +``` + +### 2.1 语句块的构成部分 + +语句块语法:由模式 (pattern) 和动作 (action) 构成。 + +```bash +awk 'pattern { actions }' # 即 模式 {动作} +# 模式 (pattern): 可以是正则表达式、关系表达式、特定条件等。 +# 动作 (action): 一组语句,描述在匹配模式的行上执行的操作 +# 示例 +awk '/王/ { print $0 }' student_scores.csv +# 打印文件中所有包含 王 的行。 +``` + +- 如果没有指定模式,则对每一行都执行动作。 +- 指定模式后,只有匹配模式的行,才执行动作,即满足条件才执行 + +### 2.2 深入理解三个语句块 + +**示例:** + +```sh +awk -F, 'BEGIN { print "----开始-----" } +$2>=80 { print } +END { print "----结束-----" } +' student_scores.csv +# 脚本解释 +# -F,:指定逗号作为字段分隔符,不指定默认是空格(\t)做分隔符 +# BEGIN { print "----开始-----" }:在处理文件开始时打印“----开始-----”。 +# $2 >= 80 { print }:对于每一行,如果第二个字段(数学成绩)大于或等于80,则打印该行。 +# END { print "----结束-----" }:在处理文件结束时打印“----结束-----”。 +``` + +**输出** + +运行上述脚本的输出如下: + +``` +----开始----- +姓名,数学,语文,科学 +张伟,85,92,88 +王强,95,89,94 +刘洋,88,94,90 +杨梅,91,87,89 +黄磊,82,90,86 +王彬,90,93,92 +----结束----- +``` + +### 2.3 术语解释 + +#### Awk 中的常用选项(限命令行) + +- `-F`:指定输入字段分隔符。例如,`-F,` 将逗号设置为字段分隔符。 + - 默认是以空格\t等为分隔符 + - 类似于FS="分隔符" 在文件中使用 + +- `-v`:赋值外部变量。例如,`-v var=value`。 min=60 +- `-f`:指定 Awk 脚本文件。例如,`-f script.awk`。 +- `-W`:控制警告和其他运行时选项。例如,`-W version` 用于显示版本信息。 + +#### Awk 中的常用模式(pattern) + +- 匹配正则表达式 + - `/pattern/`:匹配包含指定模式的行。例如,`/error/` 匹配所有包含“error”的行。 + - $2 ~ /pattern/ :匹配第2列包含关键字pattern的行, + - $2 !~ /pattern/:匹配第2列不包含关键字pattern的行 + +- 比较运算符 + - 匹配第 n 行。例如,`NR == 1` 匹配第一行。 + - `==`(等于),例$2 == "张三" 匹配第二列等于张三的行 + - `!=`(不等于) + - `<`(小于) + - `<=`(小于等于) + - `>`(大于) + - `>=`(大于等于) + +- 逻辑运算符 + - `NR >= m && NR <= n`:匹配第 m 到第 n 行。例如,`NR >= 2 && NR <= 4` 匹配第2到第4行。 + - `&&`:逻辑与(AND) + - `||`:逻辑或(OR) + - `!`:逻辑非(NOT) + +- 三元运算符 + - 条件运算符 `? :` + - grade = ($2 >= 60 ? "及格" : "不及格") + + +#### Awk 中的常用动作(action) + +- `print`:打印指定内容。例如,`print $1` 打印第一字段。 + +- `printf`:格式化输出。例如,`printf "%s\n", $1` 以格式化方式打印第一字段。 + + - 语法: + + ```bash + printf (format, expression1, expression2, ...) + # format 是一个包含格式说明符的字符串。 + # expression1, expression2, ... 是要格式化并插入到 format 字符串中的表达式。 + ``` + + - 常用的格式说明符 + + - `%s`:字符串 + - `%d`:有符号十进制整数 + - `%f`:浮点数 + + - 带有宽度和对齐的写法: + - `%5s`:字段宽度为 5 的字符串,右对齐 + - `%-5s`:字段宽度为 5 的字符串,左对齐 + - `%10d`:字段宽度为 10 的有符号十进制整数,右对齐 + - `%-10d`:字段宽度为 10 的有符号十进制整数,左对齐 + - `%8.2f`:字段总宽度为 8,其中小数点后有 2 位的浮点数 + +- `{}`:包含一个或多个动作的块。例如,`{ print $1; print $2 }`。 + + - 用;号分隔多个动作语句 + - 如果每个语句在单独的行上,;号可以省略 + + +#### Awk 中的特殊变量 + +- `NR`:表示记录的数量(当前行号)。Numbers of Rows +- `NF`:表示当前行的字段数量。`$NF`表示什么?最后一列 Number of flied +- `$0`:包含当前行的文本内容,即一整行内容。有时候也省略 +- `$1`、`$2`:表示当前行的第1个、第2个字段的内容,以次类推。 +- `FS`:输入时的域分割符。效果同-F选项 File split +- `OFS`:输出时的域分割符。out File split + +### 2.4 示例 + +#### 计算总成绩: +```sh +awk 'BEGIN { FS="," } NR > 1 { print $1, $2 + $3 + $4 }' student_scores.csv +# BEGIN { FS="," }:设置字段分隔符 FS 为逗号。 +# NR > 1:跳过标题行,只处理从第二行开始的数据。 +# { print $1, $2 + $3 + $4 }:打印每一行的第一个字段和第二到第五个字段的和。 +``` + +#### 查找姓名为`王强` 的学生信息: +```sh +awk 'BEGIN { FS="," } $1 == "王强" { print }' student_scores.csv +# 输出 王强,95,89,94,85 +``` + +#### 打印行号和学生姓名: +```sh +awk -F ',' '{ print NR, $1 }' student_scores.csv +``` + +#### 查找特定学生的成绩: +```sh +awk -F ',' '$1 == "杨梅" { print "姓名:" $1, "数学:" $2, "语文:" $3, "科学:" $4 }' student_scores.csv +``` + +#### 使用正则表达式查找学生: +```sh +awk -F ',' '$1 ~ /王强/ { print $1, $2 + $3 + $4 }' student_scores.csv +#使用 ~ 运算符明确表示匹配第一个字段 $1,即检查第一字段是否包含 "王强"。 + +#$1 == "王强":匹配完全等于 "王强" 的行。 +#$1 ~ /王强/:匹配包含 "王强" 的所有行 + +#思考:查找姓名中包含王字的学生的行 +``` + +#### 格式化输出: +```sh +# 思考以下命令得到什么格式的结果 +awk -F, '{ printf("%s 的成绩是 %.2f\n", $1, $2 + $3 + $4) }' student_scores.csv + +# 以下呢 +awk -F, 'NR>1 { printf("%s 的成绩是 %10.2f\n", $1, $2 + $3 + $4) }' student_scores.csv + +awk -F, 'NR>1 { printf("%s 的成绩是 %d\n", $1, $2 + $3 + $4) }' student_scores.csv +``` + +#### 结合模式使用: +```sh +awk -F, '$2 >= 85 || $3 >= 90 { print $0 }' student_scores.csv # 数字85以上,或数学90以上 +awk -F, '$2<60 || $3<60 || $4<60 {print} ' student_scores.csv # 有任意一科挂科的 +# 思考如何找出所有科目都及格的学生 +``` + +#### 2.5 数据验证 + +验证数据的一些示例: +```sh +awk -F, '$2 < 60 { print $0, "语文不及格" }' student_scores.csv +``` + +#### 2.6 BEGIN 语句块的作用 + +在 BEGIN 语句块中输出头信息: +```sh +awk 'BEGIN { print "我在最开头输出" } { print $0 }' student_scores.csv +``` + +设置域分隔符: +```sh +cat /etc/passwd +awk 'BEGIN { FS=":" } { print $1, $6 }' /etc/passwd # 打印用户和家目录 +``` + +#### 2.7 END 语句块 + +在 END 语句块中输出汇总信息: +```sh +awk 'END { print "共有"NR"个", "students" }' student_scores.csv # 输出共有13个 students +# 如何去掉标题的那一行 +``` + +#### 计算总成绩和平均成绩: + +```sh +awk -F, '{ total += $2 + $3 + $4 } +END { + print NR-1, "个学生"; + print "总分 is", total; + print "平均分 is", total/(NR-1) +}' student_scores.csv +``` + +### 2.8 Awk 中的控制语句 + +```sh +# 利用数组lines[]存放每一行的内容,再倒序打印每一行 +awk '{ lines[NR] = $0 } END { for (i = NR; i > 0; i--) print lines[i] }' student_scores.csv + +# 在所有不及格的分数后添加不及格三个字 +awk -v min=60 'BEGIN { FS=","; OFS="\t\t" } +{ + math=$2; + chinese=$3; + science=$4; + if (math < min) math = math " 不及格"; + if (chinese < min) chinese = chinese " 不及格"; + if (science < min) science = science " 不及格"; + print $1, math, chinese, science + }' student_scores.csv + +``` + +### 2.9 关联数组 + +使用关联数组处理数据: +```sh +# 按班级输出总分 +awk -F, 'NR>1 { scores[$5] += $2+$3+$4 } END { for (name in scores) print name, scores[name] }' student_scores.csv +``` + +打印每项中数字最大的项: +```sh +# 打印每个班级中语文最高分 +awk -F, 'NR>1 { if (scores[$5] < $2) scores[$5] = $2 } END { for (name in scores) print name, scores[name] }' student_scores.csv + +# 打印每个班语文最高分姓名和分数 +awk -F, ' +NR == 1 { next } # Skip header line +{ + class = $5 + chinese_score = $2 + if (chinese_score > max_score[class]) { + max_score[class] = chinese_score + max_name[class] = $1 + } +} +END { + for (class in max_score) { + print class, max_name[class], max_score[class] + } +} +' student_scores.csv + +``` + +### 2.10 Awk 中的内建函数 + +常用内建函数: +- `length(s)`:返回字符串 `s` 的长度。 +- `substr(s, p, n)`:返回字符串 `s` 从位置 `p` 开始的 `n` 个字符。 +- `index(s, t)`:返回字符串 `t` 在字符串 `s` 中第一次出现的位置。 +- `toupper(s)`:将字符串 `s` 转换为大写。 +- `tolower(s)`:将字符串 `s` 转换为小写。 +- `sprintf(format, expr, ...)`:返回格式化字符串。 +- `system(cmd)`:执行系统命令 `cmd`。 + +示例: +```sh +awk '{ print length($1) }' student_scores.csv +awk '{ print substr($1, 1, 3) }' student_scores.csv +awk '{ print index($1, "A") }' student_scores.csv +awk '{ print toupper($1) }' student_scores.csv +awk '{ print tolower($1) }' student_scores.csv +awk '{ print sprintf("%.2f", $2 + $3 + $4) }' student_scores.csv +awk 'BEGIN { system("date") }' +``` + +### 2.11 常见使用场景 + +#### 删除空行: +```sh +# NF代表有列的行,即排除了空行 +awk 'NF' student_scores.csv +``` + +#### 查找和替换文本: +```sh +#所有的"1班"替换为"一班" +awk -F, ' { gsub(/1班/, "一班"); print }' student_scores.csv +``` + +#### 结合其他命令: +```sh +cat student_scores.csv | awk -F, '{ print $1 }' +grep "王" student_scores.csv | awk -F, '{ print $1,$2 }' +awk -F, '{ print $5 }' student_scores.csv | sort | uniq +``` + +## 3. 工作中的使用场景 + +在实际工作中,AWK 常用于处理文本文件和数据文件,特别是结构化数据的分析和处理。以下是几个常见的使用场景和具体示例: + +### 1. 数据提取与处理 + +#### 示例 1.1:从日志文件中提取特定信息 + +假设有一个访问日志文件 `access.log`,格式如下: + +``` +192.168.1.1 - - [10/Oct/2021:13:55:36] "GET /index.html HTTP/1.1" 200 1234 +192.168.1.2 - - [10/Oct/2021:13:56:12] "POST /login HTTP/1.1" 302 2345 +``` + +**需求:提取每个访问记录中的 IP 地址和请求的 URL** + +```sh +awk '{ print $1, $7 }' access.log +``` + +输出: + +``` +192.168.1.1 /index.html +192.168.1.2 /login +``` + +#### 示例 1.2:统计 CSV 文件中某一列的总和 + +假设有一个销售记录文件 `sales.csv`,内容如下: + +``` +日期,产品,数量,单价 +2023-05-01,产品A,10,5.00 +2023-05-01,产品B,5,8.00 +2023-05-02,产品A,7,5.00 +2023-05-02,产品C,3,15.00 +``` + +**需求:计算销售的总金额** + +```sh +awk -F, 'NR > 1 { total += $3 * $4 } END { print "Total Sales:", total }' sales.csv +``` + +输出: + +``` +Total Sales: 190 +``` + +### 2. 数据筛选与过滤 + +#### 示例 2.1:筛选出特定条件的数据行 + +假设有一个员工记录文件 `employees.csv`,内容如下: + +``` +姓名,职位,部门,工资 +张伟,经理,市场部,12000 +李娜,助理,财务部,8000 +王强,工程师,技术部,10000 +赵敏,秘书,市场部,7000 +``` + +**需求:筛选出工资超过9000的员工** + +```sh +awk -F, '$4 > 9000 { print $1, $4 }' employees.csv +``` + +输出: + +``` +张伟 12000 +王强 10000 +``` + +#### 示例 2.2:根据正则表达式匹配筛选数据 + +**需求:筛选出属于市场部的员工** + +```sh +awk -F, '$3 ~ /市场部/ { print $1, $3 }' employees.csv +``` + +输出: + +``` +张伟 市场部 +赵敏 市场部 +``` + +### 3. 数据格式转换 + +#### 示例 3.1:将数据从逗号分隔转换为制表符分隔 + +假设有一个 CSV 文件 `data.csv`,内容如下: + +``` +name,age,city +Alice,30,New York +Bob,25,Los Angeles +Charlie,35,Chicago +``` + +**需求:将数据从逗号分隔转换为制表符分隔** + +```sh +awk -F, '{ OFS="\t"; print $1, $2, $3 }' data.csv +``` + +输出: + +``` +name age city +Alice 30 New York +Bob 25 Los Angeles +Charlie 35 Chicago +``` + +### 4. 生成报告 + +#### 示例 4.1:生成学生成绩报告 + +假设有一个学生成绩文件 `student_scores.csv`,内容如下: + +``` +姓名,数学,语文,科学,班级 +张伟,85,92,88,1班 +李娜,78.5,81,79.5,2班 +王强,95,89,94.5,3班 +赵敏,90,72.5,70,2班 +刘洋,88,94,90,1班 +``` + +**需求:生成每个学生的总成绩报告** + +```sh +awk -F, 'NR > 1 { total = $2 + $3 + $4; print $1, total }' student_scores.csv +``` + +输出: + +``` +张伟 265 +李娜 239 +王强 278.5 +赵敏 232.5 +刘洋 272 +``` + +### 5. 系统管理与维护 + +#### 示例 5.1:处理系统配置文件 + +假设有一个系统配置文件 `config.txt`,内容如下: + +``` +# 系统配置 +hostname=myserver +ip_address=192.168.1.1 +netmask=255.255.255.0 +gateway=192.168.1.254 + +# This is a comment +email=john@example.com +location=USA +``` + +**需求:提取配置项的名称和对应的值** + +```sh +awk -F= '/^[^#]/ { print $1, $2 }' config.txt # [^#] 排除#开头的行 +sed "/#/d" 2.txt | awk -F= 'NF {print $1,$2}' +``` + +输出: + +``` +hostname myserver +ip_address 192.168.1.1 +netmask 255.255.255.0 +gateway 192.168.1.254 +email john@example.com +location USA +``` + +#### 示例 5.2:分析系统资源使用情况 + +假设有一个磁盘使用情况文件 `disk_usage.txt`,内容如下: + +``` +Filesystem Size Used Avail Use% Mounted on +/dev/sda1 50G 20G 30G 40% / +/dev/sda2 100G 50G 50G 50% /home +/dev/sda3 200G 150G 50G 75% /var +``` + +**需求:查找使用率超过50%的文件系统** + +```sh +awk '$5+0 > 50 { print $1, $5 }' disk_usage.txt +# $5+0 是将字符串车为数字的小技巧 +``` + +输出: + +``` +/dev/sda3 75% +``` + +这些示例展示了AWK在日常工作中的多种实际应用场景,从数据提取、筛选、格式转换到生成报告和系统管理。AWK强大的文本处理能力使其成为处理结构化文本数据的利器。 + +实际案例: + +```bash +/var/log/nginx/access.log +``` + +awk、grep、sed是linux操作文本的三大利器,合称*文本三剑客* + +**grep**:主要用于查找匹配特定模式的行。强大之处在正则匹配 + +**sed**:主要用于对文本进行替换、删除、插入等编辑操作。 + +**awk**:功能强大的文本处理语言,适用于复杂的数据处理和报告生成。强于对结构化数据的处理 + + + +作业: + +1. 只显示/etc/passwd的账户 + + ```sh + awk -F':' '{ print $1 }' /etc/passwd + ``` + + + +2. 只显示/etc/passwd的账户和对应的shell,并在第一行上添加列名用户制表符shell,最后一行添加---------------- + + ```sh + awk -F':' 'BEGIN{ print "用户\tshell"}{print $1 "\t" $7} END{print "----------------"}' etc/passwd + - -F':'`:指定字段分隔符为冒号 (`:`)。 + - `BEGIN { print "用户\tShell" }`:在处理文件之前,打印表头 "用户" 和 "Shell",并用制表符 (`\t`) 分隔。 + - `{ print $1 "\t" $7 }`:对于每一行,打印第一个字段(用户名)和第七个字段(shell),并用制表符分隔。 + - `END { print "----------------" }`:在处理完所有行之后,打印分隔线 "----------------"。 + ``` + + - ` + +3. 搜索/etc/passwd有关键字root的所有行 + + ```sh + awk -F ':' '/root/{print}' etc/passwd + + ``` + + + +4. 统计/etc/passwd文件中,每行的行号,每列的列数,对应的完整行内容以制表符分隔 + + ```sh + awk -F':' '{ print NR "\t" NF "\t" $0 }' /etc/passwd + ``` + + + +5. 输出/etc/passwd文件中以nologin结尾的行\ + + ```sh + awk -F ':' '/nologin$/{print}' etc/passwd + ``` + + + +6. 输出/etc/passwd文件中uid字段小于100的行 + + ```sh + awk -F':' '$3 < 100 { print }' /etc/passwd + ``` + + + +7. /etc/passwd文件中gid字段大于200的,输出该行第一、第四字段,第一,第四字段并以制表符分隔 + + + + ``` + awk -F ':' ' $4 > 200 {print $1 "\t" $4}' etc/passwd + ``` + +8. 输出/etc/passwd文件中uid字段大于等于100的行 + + + + ```sh + awk -F':' '$3 >= 100 { print }' /etc/passwd + ``` + + + +9. + + +