diff --git "a/26 \351\231\210\347\202\263\351\221\253/20240603 awk\347\273\203\344\271\240.md" "b/26 \351\231\210\347\202\263\351\221\253/20240603 awk\347\273\203\344\271\240.md" new file mode 100644 index 0000000000000000000000000000000000000000..c0b8a76046521cbac93b9976c24910703014db7f --- /dev/null +++ "b/26 \351\231\210\347\202\263\351\221\253/20240603 awk\347\273\203\344\271\240.md" @@ -0,0 +1,655 @@ +# 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的账户 + + ```shell + awk -F : '{print $1}' /etc/passwd + ``` + +2. 只显示/etc/passwd的账户和对应的shell,并在第一行上添加列名用户制表符shell,最后一行添加---------------- + + ```shell + awk -F : 'BEGIN {print "用户名\tshell"} {print $1,$7} END {print "----------------"}' /etc/passwd + ``` + +3. 搜索/etc/passwd有关键字root的所有行 + + ```shell + awk -F : '/root/ {print $0}' /etc/passwd + ``` + +4. 统计/etc/passwd文件中,每行的行号,每列的列数,对应的完整行内容以制表符分隔 + + ```shell + awk -F ":" '{OFS="\t";print NR,NF,$0}' /etc/passwd + ``` + +5. z输出/etc/passwd文件中以nologin结尾的行 + + ```shell + awk -F : '/nologin$/ {print $0}' /etc/passwd + ``` + +6. 输出/etc/passwd文件中uid字段小于100的行 + + ```shell + awk -F : '$3<100 {print $0}' /etc/passwd + ``` + +7. /etc/passwd文件中gid字段大于200的,输出该行第一、第四字段,第一,第四字段并以制表符分隔 + + ```shell + awk -F : '$4>200 {OFS="\t";print $1,$4}' /etc/passwd + ``` + +8. 输出/etc/passwd文件中uid字段大于等于100的行 + + ```shell + awk -F : '$3>=100 {print $0}' /etc/passwd + ``` diff --git "a/26 \351\231\210\347\202\263\351\221\253/20240604 vim_vi\347\273\203\344\271\240.md" "b/26 \351\231\210\347\202\263\351\221\253/20240604 vim_vi\347\273\203\344\271\240.md" new file mode 100644 index 0000000000000000000000000000000000000000..9a4ac5e8fd53e2ff7faf586a70dcf370b701f29d --- /dev/null +++ "b/26 \351\231\210\347\202\263\351\221\253/20240604 vim_vi\347\273\203\344\271\240.md" @@ -0,0 +1,148 @@ +1. vi 编辑器有几种模式? + + ``` + 刚进入vi 时的默认模式。这个模式下能够进行:移动光标、整行的复制粘贴、整行删除 等基本操作。 + #编辑模式: + + #命令行模式(末行模式): + 在一般指令模式下,按 ":" "/" "?" 均可进入命令行模式。由于此模式的输入会显示在窗口的最后一行,也叫末行模式。此模式下能够进行:搜索、保存、离开 等操作。 + ``` + +2. 如何进入 vi 编辑器的插入模式 + + ``` + 在一般指令模式下,按 "a" "i" "o" 均可进入编辑模式。此模式下能够进行:文本的输入、删除。 + ``` + +3. 如何进入 vi 编辑器的可视化模式 + + ``` + 在Vim命令模式下,输入 v 或者 V 或者 Ctrl + v 都可进入可视化模式,这三个Vim可视化模式的主要区别在于: + 字符选择模式: 选中光标经过的所有字符,普通模式下按 v 进入 + 行选择模式:选中光标经过的所有行,普通模式下按 V 进入 + 块选择模式:选中一整个矩形框表示的所有文本,普通模式下按 Ctrl + v 进入 + ``` + +4. 在 vi 编辑器中如何复制一行 + + ``` + yy -- 复制当前行 + 可以鼠标拖动选择复制 按y复制选中的 + ``` + +5. 在 vi 编辑器中如何进行粘贴 + + ``` + 按p粘贴 + 在当前光标之前粘贴按大写P + ``` + +6. 如何删除从 3 行到 15 行的所有数据 + + ``` + //删除行范围 + 在命令行模式下, + :3,15d + ``` + +7. vim练习: + + - 光标移动练习,命令模式下: + + - 单位级 h j k l + - 单词级 w e b + - 块级 gg G 0 ^ $ H M L ngg nj nk + + 把下列句子按照第一句的正确顺序修改好并把多余的空行删除 + + ``` + :/^$/d- 删除空行 + ``` + + + + ``` + this is a simple easy vim tutorial + + tutorial simple a easy this vim is + is this tutorial vim simple a easy + + + tutorial vim this is a easy simple + tutorial easy vim simple a this is + simple a vim easy tutorial is this + + tutorial is easy vim a simple this + + + vim simple this tutorial a easy is + a vim tutorial simple easy is this + + + easy a simple vim is tutorial this + vim tutorial is a easy simple this + a this vim tutorial is easy simple + this tutorial simple easy a is vim + + + easy tutorial this simple a is vim + a tutorial easy is this simple vim + + a tutorial vim is easy this simple + simple this easy is vim tutorial a + + this tutorial is a easy simple vim + vim is tutorial simple this easy a + + vim is simple this tutorial easy a + easy a simple is vim this tutorial + vim is tutorial simple a easy this + this vim is tutorial simple easy a + ``` + + 先敲出以下代码,然后修正以下代码中的错误单词、重复单词、错误格式、多余行,修改函数名为 typing 并为定时器添加 300 毫秒延迟 + + ``` + const bbb = () => { + // this is is a description + // + // another descriptttion + const timer = setTimeout(( ) => { + console.log(that) alert('cool!') + // awosome man ! + }) + } + ``` + + 尝试在下面的文本中进行复制粘贴练习 + + ``` + 删除这一行 + 粘贴到这一行下面 + 剪切 ABC 并把它粘贴到 XYZ 前面,使这部分内容看起来像 + 剪切 并把它粘贴到 ABC XYZ 前面。 + ``` + + 尝试修改下列文本的大小写 + + ``` + Change this line to UPPERCASE, THEN TO lowercase. + ``` + + 按下面的说明进行操作 + + ``` + 按 dd 删除本行 + 按 . 重复删除操作 + 2. 再删除两行 + 这行也没了 + p 把刚才删掉的粘回来 + 3. 又多出 6 行 + ``` + + 左缩进、右缩进练习 + + ``` + 在这一行上依次按 3>>,<< 和