[命令] Linux 命令 sed (显示或修改文件的行)

内容一:sed 基础

1.1 sed 的工作原理

1) 从第 1 行开始 1 行 1 行地读取文件里的内容
2) 每读取 1 行就将内容存入到 pattern space (模型空间) 里面,pattern space (模型空间) 里的内容默认会自动显示出来
3) 在 pattern space (模型空间) 中执行 sed 命令
4) 再显示 pattern space (模型空间) 中的内容然后将其清空
5) 之后重复以上操作再开始读取文件里的下 1 行
6) pattern space (模型空间) 里的内容可以存储到 hold space 里面

1.2 sed 格式

# sed <option> <parameter> <file>

1.3 sed 选项

1) -n 或 –quiet 或 –silent 仅显示处理后的内容,没处理的内容就不显示了,也就是仅显示非标准输出的内容
2) -i 实现数据的变更
3) -e <script> 或 –expression=<script> 这样可以一次匹配多个参数
4) -r 支持扩展正则
5) -f <script file> 或 –file=<script file> 以指定的脚本文件来处理输入的文件
6) -h 或 –help 显示帮助内容
7) -V 或 –version 显示版本信息


补充:
1) pattern space (模型空间) 相当于处理内容的流水线
2) hold space (保持空间) 相当于暂时存储内容的仓库

1.4 sed 的参数

1) p 先将每行的内容以标准输出的形式显示 1 遍,再将每行的内容以非标准输出的形式再显示 1 遍。不改变是否跳过下 1 行的属性,也就是如果前面有一个 N 参数,则跳过往下第 1 行接着执行往下第 2 行,如果前面没有任何参数,则直接执行往下第 1 行
2) P 先将每行的内容以非标准输出的形式再显示 1 遍,但是如果是 2 行组合在一起的话只显示第 1 行的内容,也就是只显示分行符 “/n” 之前的内容,再将每行的内容以标准输出的形式显示 1 遍。不改变是否跳过下 1 行的属性,也就是如果前面有一个 N 参数,则跳过往下第 1 行接着执行往下第 2 行,如果前面没有任何参数,则直接执行往下第 1 行
3) n 将每行的内容以标准输出的形式显示 1 遍,并提前读取下 1 行的内容,将下 1 行的内容覆盖当前行的 pattern space (模型空间),也就是下 1 行变成了当前行,再将当前行的内容以标准输出的形式显示一遍 (如果此时得到的内容是非标准的形式则不显示)。然后跳过往下第 1 行接着执行往下第 2 行。另外,如果往下 1 行不存在则直接停止所有命令,如果当前面的条件匹配时则放弃所有后面的命令
4) N 提前预读下 1 行,将下 1 行的内容添加到本行的 pattern space (模型空间) 后面,但是本行的内容和下 1 行的内容之间依旧存在分行符 “/n”,再将当前行的内容以标准输出的形式显示一遍。然后跳过下第 1 行接着执行往下第 2 行。另外,如果下 1 行不存在则以标准的形式显示当前 pattern space (模型空间) 里的内容并直接停止所有命令,如果当前面的条件不匹配时则放弃执行 N 命令直接执行后面的命令
5) = 以非标准输出的形式显示每 1 行是第几行,在行数后面以标准输出的形式显示行的内容
6) a 在某 1 行后面添加 1 行的内容
7) i 在某 1 行前面插入 1 行的内容
8) c 替换某 1 行的内容
9) g 将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除
10) G 将 hold space (保持空间) 中的内容 append (添加) 到 pattern space (模型空间) 分行符 “/n” 后
11) h 将 pattern space (模型空间) 中的内容拷贝到 hold space (保持空间) 中,原来的 hold space (保持空间)里的内容被清除
12) H 将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后
13) d 删除 pattern space (模型空间) 中的所有行,并执行下 1 行
14) D 删除 pattern space (模型空间) 中的第 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行
15) s 替换某行的某些内容
16) y 替换某行的某些内容,和 s 的作用一样,但是功能没有 s 丰富
17) x 将当前 hold space (保持空间) 和 pattern space (模型空间) 内容互换,hold space (保持空间) 用来保存临时内容, pattern space (模型空间) 是本行的内容,注意 hold space (保持空间) 和 pattern space (模型空间) 的内容互换并不改变这些内容是第几行的,例如在 pattern space (模型空间) 里的内容是第 3 行的,则需要 3p
18) r 读取某一个文件里的内容
19) w 将内容写入一个文件
20) q 退出,前面可以跟退出的条件,后面可以跟退出代码
21) b 参数,设置标签分支,如果条件不成立,则对标签的位置进行跳转,如果标签省略了,则分支会一直到末尾
22) t 参数,设置标签分支,如果条件成立,则对标签的位置进行循环跳转,直到所有可进行的操作都完成后才停止,如果标签省略了则标签分支会一直到头部。在 t 和变迁之间的步骤是会循环执行的区域


注意:
1) 当多个参数进行组合时,多次标准输出最后会合并成只显示 1 次
2) 当多个参数进行组合时,当 1 个参数后面还有参数时,产生的输出不会直接显示而是传输给下 1 个参数进行处理,如果此次显示是非输出的形式,传递给下 1 个参数处理以后也会以非输出的形式输出

内容二:sed 使用 p 参数、P 参数、n 参数、N 参数和 = 参数显示某些行数据的案例

2.1 显示第 2 行的内容

# sed -n 2p test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.2 显示第 2 行到第 4 行的内容

# sed -n 2,4p test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.3 显示不是第 1 行的内容

# sed 1!p test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.4 显示最后 1 行的内容

# sed -n '$p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

或者:

# sed 'N;D' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.5 显示包含 eternalcenter 的内容

# sed -n '/eternalcenter/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.6 显示以 eternalcenter 结尾的内容

# sed -n '/eternalcenter$/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.7 显示包含 eternalcenter 的行的行号,并将此行的上一行的内容,本行的内容和下一行的内容显示出来

# sed -n -e '/eternalcenter/{=;x;1!p;g;$!N;p;D;}' -e h test.txt


补充:
1) 当执行到包含 eternalcenter 内容的行时
2) 执行 /eternalcenter/ 判断当前包含 eternalcenter 内容则为真,如果为真,执行 = 参数,以非标准输出的形式传输每 1 行是第几行,在行数后面以标准输出的形式传输行的内容,此时这些内容存放在 pattern space (模型空间) 中并传输给 x 参数
3) 执行 x 参数,将当前 hold space (保持空间) 和 pattern space (模型空间) 内容互换,并传输给 p 参数。在执行 x 参数前 hold space (保持空间) 里的内容是由上一行执行 h 参数时保存的,所以执行 x 参数后 pattern space (模型空间) 的内容是上一行的内容。注意 hold space (保持空间) 和 pattern space (模型空间) 的内容互换并不改变这些内容是第几行的
4) 执行 1! 判断当前行不是第 1 行则为真,如果不是第 1 行,如果执行 p 参数,将此时 pattern space (模型空间) 的内容以标准输出的形式进行显示,再将 pattern space (模型空间) 的内容以非标准输出的形式传输给 g 参数
5) 执行 g 参数,将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除,将此时的 pattern space (模型空间) 的内容传输给 N 参数
6) 执行 $! 判断当前行不是最后 1 行则为真,如果为真,执行 N 参数,将下一行的内容添加到本行内容的后面,但是两行之间存在分行符 “/n”,保存到 pattern space (模型空间),将此时的 pattern space (模型空间) 的内容以非标准输出的形式传输给 p 参数
7) 执行 p 参数,将 pattern space (模型空间) 里的内容以标准的形式显示,再将 pattern space (模型空间) 里的内容以非标准的形式传输给 D 参数
8) 执行 D 参数,删除 pattern space (模型空间) 中的第 1 行,也就是分行符 “/n” 之前的内容,并将此时 pattern space (模型空间) 里的内容传输给参数 h
9) 执行 h 参数,将 pattern space (模型空间) 中的内容拷贝到 hold space (保持空间) 中,原来的 hold space (保持空间)里的内容被清除
10) 这里的 test.txt 是要被 sed 操作的测试文件

2.8 显示包含从 2000 到 2007 点行

# sed -n '/2000/,/2007/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

(注意:test.txt 里必须要有包含 2000 内容和 2007 内容的行此命令才会生效)

2.9 显示从 2022-01-01 00:00:00 到 2022-02-22 00:00:00 的内容

# sed -n  '/2022-01-01 00:00:00/,/2022-02-22 00:00:00/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

(注意:test.txt 里必须要有包含 2022-01-01 00:00:00 内容和 2022-02-22 00:00:00 内容的行此命令才会生效)

2.10 显示从 2022-01-01 00:00:00 到最后的内容

# sed '/2019-01-01/,$' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

(注意:test.txt 里必须要有包含 2019-01-01 内容的行此命令才会生效)

2.11 显示总行数

# sed -n $= test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.12 以非标准输出的方式显示每 1 行是第几行,在行数后面以标准输出的方式显示行的内容,但是不显示第 1 行的内容

# sed -n '{=;1!p}' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.13 以非标准输出的方式显示每 1 行是第几行,在行数后面以标准输出的方式显示行的内容,但是不显示第 1 行的内容

# sed -n '{=;2!p}' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.14 添加行号并显示所有内容

# sed = test.txt | sed 'N;s/\n/:/'

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.15 显示奇数行

# sed -n 'p;n' test.txt


补充:
1) 执行第 1 行,执行 p 参数,将第 1 行的内容以标准输出的形式进行显示,再将第 1 行的内容以非标准输出的形式传输给 n 参数,再执行 n 参数,将第 1 行的内容以标准输出的形式进行显示 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里变为以非标准的形式进行显示),再将第 1 行的内容被第 2 行的内容覆盖,再将第 1 行的内容以标准输出的形式显示 1 遍 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里不再进行显示),再跳过第 2 行再直接执行第 3 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
2) 执行第 3 行,执行 p 参数,将第 3 行的内容以标准输出的形式进行显示,再将第 3 行的内容以非标准输出的形式传输给 n 参数,再执行 n 参数,将第 3 行的内容以标准输出的形式进行显示 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里变为以非标准的形式进行显示),再将第 3 行的内容被第 4 行的内容覆盖,再将第 1 行的内容以标准输出的形式显示 1 遍 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里不再进行显示),再跳过第 4 行再直接执行第 5 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
3) 以此类推
4) 当执行到最后 1 行时,按照之前的逻辑,如果是奇数行则显示当前行的内容,如果是偶数行则跳过
5) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed -n '$!N;P' test.txt


补充:
1) $! 作用是判断当前行不是最后 1 行时执行后面的命令
2) 执行第 1 行,执行 $! 判断当前行不是最后 1 行则为真,第 1 行不是最后 1 行,执行 N 命令,将第 2 行的内容添加到第 1 行的内容的后面,但是第 1 行和第 2 行之间存在分行符 “/n”,再将当前行的内容以标准输出的形式传输给 P 参数,再执行 P 命令,将此时第 1 行的内容以标准输出的形式进行显示,再将分行符 “/n” 之前的所有内容 (也就是最早是第 1 行的内容) 以非标准输出的形式显示出来,并跳过第 2 行再直接执行第 3 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
3) 执行第 3 行,执行 $! 判断当前行不是最后 3 行则为真,第 3 行不是最后 1 行,执行 N 命令,将第 4 行的内容添加到第 3 行的内容的后面,但是第 3 行和第 4 行之间存在分行符 “/n”,再将当前行的内容以标准输出的形式传输给 P 参数,再执行 P 命令,将此时第 3 行的内容以标准输出的形式进行显示,再将分行符 “/n” 之前的所有内容 (也就是最早是第 3 行的内容) 以非标准输出的形式显示出来,并跳过第 3 行再直接执行第 4 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
4) 以此类推
5) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
6) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed 'n;d' test.txt


补充:
1) 执行第 1 行,执行 n 参数,将第 1 行的内容以标准输出的形式进行显示,再将第 1 行的内容被第 2 行的内容替代,再将被替代的内容传输给 d 参数,再执行 d 参数将第 2 行的内容删除掉。并跳过第 2 行直接执行第 3 行
2) 执行第 3 行,执行 n 参数,将第 3 行的内容以标准输出的形式进行显示,再将第 3 行的内容被第 4 行的内容替代,再将被替代的内容传输给 d 参数,再执行 d 参数将第 4 行的内容删除掉。并跳过第 4 行直接执行第 5 行
3) 以此类推
4) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
5) 这里的 test.txt 是要被 sed 操作的测试文件

2.16 显示偶数行

# sed -n 'n;p' test.txt


补充:
1) 执行第 1 行,执行 n 参数,将第 1 行的内容以标准输出的形式进行显示,再将第 1 行的内容被第 2 行的内容替代,再将被替代的内容传输给 p 参数,再执行 p 参数,将第 2 行的内容以标准的形式显示,再将第 2 行的内容以非标准的形式显示。并跳过第 2 行再直接执行第 3 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
2) 执行第 3 行,执行 n 参数,将第 3 行的内容以标准输出的形式进行显示,再将第 3 行的内容被第 4 行的内容替代,再将被替代的内容传输给 p 参数,再执行 p 参数,将第 4 行的内容以标准的形式显示,再将第 4 行的内容以非标准的形式显示。并跳过第 4 行再直接执行第 5 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
3) 以此类推
4) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
5) 这里的 test.txt 是要被 sed 操作的测试文件

2.17 显示所有行是第几行,行数后面跟行的内容

# sed = test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.18 显示所有行是第几行,只显示行数

# sed -n test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.19 显示包含 eternalcenter 内容的行是第几行

# sed -n '/eternalcenter/=' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.20 显示所有空行是第几行

# sed -n '/^$/=' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.21 合并相同的行,也就是相同行去重

# sort test.txt | sed '$!N;/^\(.*\)\n\1$/!P;D'

(
补充:
1) 执行 sort 将所有的行进行排序将相同的行排在一起,并结果输出给 sed
2) 执行第 1 行,执行 $! 判断当前行不是最后 1 行则为真,第 1 行不是最后 1 行,执行 N 参数,将第 2 行的内容添加到第 1 行的内容的后面,但是第 1 行和第 2 行之间存在分行符 “/n”,再将组合后的内容传输给 P 参数,再执行 /^(.*)\n\1$/! 判断当前行 \n 前面和后面不一样则为真,当为假时则直接不执行将内容传输给 D 参数,当为真时,执行 P 参数,先将第 1 行的内容以非标准输出的形式显示一遍,但是如果是 2 行组合在一起的话只显示第 1 行的内容,也就是只显示分行符 “/n” 之前的内容,再将第 1 行的内容以标准输出的形式传输给 D 参数,执行 D 参数,删除内容中的第 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行第 2 行,如果删除后没有内容了,则跳过第 2 行直接执行第 3 行
3) 以此类推
4) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
5) 这里的 test.txt 是要被 sed 操作的测试文件
)

内容三:sed 使用 a 参数在某 1 行后面添加数据的案例

3.1 在第 2 行后面添加 1 行 eternalcenter

# sed "2a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.2 在 abc 那 1 行后面添加 1 行 eternalcenter

# sed "/abc/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.3 在 test.txt 文件里在以 a 开头的行后面添加 eternalcenter

# sed "/^a/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.4 在以 [a] 开头的行后面添加 eternalcenter

# sed "/^\[a\]/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.5 在最后 1 行后面添加 eternalcenter

# sed "$a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.6 在 eternalcenter 这 1 行后面添加 eternalcentre、mingyuzhu 和 zhumingyu 3 行

# sed '/eternalcenter/a eternalcentre\nmingyuzhu\nzhumingyu' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容四:sed 使用 i 参数在某 1 行前面插入数据的案例

4.1 在第 2 行前面插入 1 行 eternalcenter

# sed "2i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.2 在 abc 那 1 行前面插入 1 行 eternalcenter

# sed "/abc/i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.3 在以 a 开头的行前面插入 eternalcenter

# sed "/^a/i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.4 在以 [a] 开头的行前面插入 eternalcenter

# sed "/^\[a\]/i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.5 在最后 1 行后面添加 eternalcenter

# sed "$a eternalcenter" test.txt

或者:

# sed "/$/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容五:sed 使用 c 参数替换某些行数据的案例

5.1 将第 2 行替换成 eternalcenter

# sed "2c\ eternalcenter" test.txt

或者:

# sed "2ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.2 将 abc 那 1 行替换成 eternalcenter

# sed "/abc/c\ eternalcenter" test.txt

或者:

# sed "/abc/ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.3 将以 a 开头的那 1 行替换成 eternalcenter

# sed "/^a/c\ eternalcenter" test.txt

或者:

# sed "/^a/ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.4 将以 [a] 开头的那 1 行替换成 eternalcenter

# sed "/^\[a\]/c\ eternalcenter" test.txt

或者:

# sed "/^\[a\]/ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.5 将以 eternalcentre 开头的那 1 行替换成 eternalcenter ALL=(ALL) NOPASSWD: ALL

# sed "/^eternalcentre/c\ eternalcenter\ ALL=\(ALL\)\ NOPASSWD:\ ALL" test.txt

或者:

# sed "/^eternalcentre/c\eternalcenter\ ALL=\(ALL\)\ NOPASSWD:\ ALL" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容六:sed 使用 d 参数删除某些行的案例

6.1 删除第 2 行

# sed 2d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.2 删除包含 abc 的那 1 行

# sed /"abc"/d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.3 删除以 a 开头的那 1 行

# sed /"^a"/d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.4 删除以 [a] 开头的那 1 行

# sed /"^\[a\]"/d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.5 删除所有被注释掉的行

# sed '/^#/d' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.6 删除第 2 行到第 4 行

# sed '2,4d' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容七:sed 使用 s 或者 y 参数替换某些行某些数据的案例

7.1 将所有行的第一个 eternalcentre 换成 eternalcenter

# sed 's/eternalcentre/eternalcneter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.2 将所有行的所有 eternalcentre 换成 eternalcenter

# sed 's/eternalcentre/eternalcneter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.3 将包含 eternalcentre 的行换成 eternalcenter

# sed 's/.*eternalcentre.*/eternalcneter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.4 将第 3 行的第 2 个 eternalcentre 换成 eternalcenter

# sed '3s/eternalcentre/eternalcenter/2' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.5 在所有行前面添加 eternalcenter

# sed 's/^/eternalcenter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

或者:

# sed 's/^/eternalcenter&/g test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.6 在所有行后面添加 eternalcenter

# sed 's/$/eternalcenter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

或者:

# s/$/&eternalcenter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.7 将第 2 行替换成 eternalcenter

# sed '2s/.*/eternalcenter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.8 将最后 1 行替换成 eternalcenter

# sed '$s/.*/eternalcenter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.9 将第 2 到第 3 行替换成 eternalcenter

# sed '2,3s/.*/eternalcenter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.10 将 eternalcenter 前面的数据和后面的数据对调位置

# sed "s/^\(.*\)\(eternalcenter\)\(.*\)$/\3\2\1/" test.txt

或者:

# sed -r "s/(.*)(eternalcenter)(.*)/\3\2\1/" test.txt

或者:

# sed -r "s/(.*)((eternal)(center))(.*)/\5\2\1/" test.txt

(补充:这里以 (.*) 是 1,((eternal)(center)) 是 2,(eternal) 是 3,(center) 是 4,(.*) 是 5 为例,这里的 test.txt 是要被 sed 操作的测试文件)

7.11 所有行前后字符串互调

# sed -r 's/.*/&\n/g;:a;s/(.*)(.)\n(.*)/\1\n\3\2/g;ta;s/^\n//g' test.txt


补充:
1) 执行 s/.*/&\n/g 在每行行尾添加换行符 “\n”
2) 执行 a 参数和 ta 参数,将从 :a 到 ta 之间的内容以后面的操作作为循环条件只要还能继续执行就一直循环,每次执行 s/(.*)(.)\n(.*)/\1\n\3\2/g,将此时所有行换行符 “\n” 之前的内容里最后一个字母移动到所有行换行符 “\n” 之后到内容里最后一个位置
3) 执行 s/^\n//g 删除所有行的换行符 “\n”
4) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed -r 's/.*/&\n/g;:b;s/(.*)(.)\n(.*)/\1\n\3\2/g;tb;s/^\n//g' test.txt


补充:
1) 执行 s/.*/&\n/g 在每行行尾添加换行符 “\n”
2) 执行 b 参数和 tb 参数,将从 :b 到 tb 之间的内容以后面的操作作为循环条件只要还能继续执行就一直循环,每次执行 s/(.*)(.)\n(.*)/\1\n\3\2/g,将此时所有行换行符 “\n” 之前的内容里最后一个字母移动到所有行换行符 “\n” 之后到内容里最后一个位置
3) 执行 s/^\n//g 删除所有行的换行符 “\n”
4) 这里的 test.txt 是要被 sed 操作的测试文件

7.12 删除每行的第 1 个字符和最后 1 个字符

# sed 's/.//1;s/.$//' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.13 删除每行的第 2 个字符和最后 1 个字符

# sed 's/.//2;s/.$//' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.14 删除所有行的数字和空格

# sed -r 's/[0-9]//g;s/^( )+//g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.15 删除 1 行不是最后 1 行包含 eternalcenter 的行

# sed '/eternalcenter/N;s/.*\n//' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.16 将所有的大写字母都添加括号

# sed 's/[A-Z]/(&)/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.17 在第 7 行和第 9 行前面添加 “#”

# sed '7,9s/^/#/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.18 删除第 1 个空格

# sed -r 's/( )(.*)/\2/'

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.19 在以打写字母开头,中间是小写字母,以数字结尾的行里在小写字母和数字之间插入 ok

# sed -r 's/^([A-Z]{1,})([a-z]{1,})([0-9]{1,})$/\1\2ok\3/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.20 在以打写字母开头,中间是小写字母,以数字结尾的行里在小写字母和数字之间插入 ok,并将数字替换到最前面

# sed -r 's/^([A-Z]{1,})([a-z]{1,})([0-9]{1,})$/\3 ok \1\2/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.21 删除 2022-01-01 字符里的横杠 “-”

# sed -r 's/([0-9]{1,})\-([0-9]{1,})\-([0-9]{1,})/\1\2\3/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.22 将日期的年月日以空格分开

# echo 20220222 | sed -r 's/(....)(..)(..)/\1 \2 \3/'

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.23 合并上下行

# sed '$!N;s/\n/ /' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.24 将以反斜杠号 “\” 结尾的行与下 1 行合并在一起并以空格进行分隔 (只合并一次)

# sed '/\\$/N;s/\\\n/ /' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.25 将以反斜杠号 “\” 结尾的行与下 1 行合并在一起并以空格进行分隔 (能合并多少次就合并多少次)

# sed -e :a -e '/\\$/N;s/\\\n/ /; ta' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.26 若某行以 = 开头则合并到上 1 行并将等于号 “=” 替换成空格

# sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D' num.txt 


补充:
1) 执行 :a 参数和 ta 参数,将从 :a 到 ta 之间的内容以后面的操作作为循环条件只要还能继续执行就一直循环,每次执行 $!N;s/\n=/ /,当不是最后 1 行时,将下 1 行的内容添加到本行后面,但是本行的内容和下 1 行的内容之间依旧存在分行符 “/n”,如果此时存在 \n=,则将其替换成空格,再将最后的结果以非标准输出的方式传输给 P 参数,执行 P 参数,将分行符 “/n” 之前的所有内容 (也就是最初此行的内容) 以非标准输出的形式显示,再将此时的所有内容以非标准输出的形式传输给 D 参数,执行 D 参数,删除前 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行,否则跳过往下第 1 行接着执行往下第 2 行
2) 以此类推
3) 执行 s/^\n//g 删除所有行的换行符 “\n”
4) 这里的 test.txt 是要被 sed 操作的测试文件

7.27 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是 1 个 5 个字符长度的字节)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.....\).*\n\1.*/s/^\(.*\)\(\n\)\(.....\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

7.28 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以 .com 结尾)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\.com\).*\n\1.*/s/^\(.*\)\(\n\)\(.*\.com\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

7.29 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以空格 “ ” 结尾)

# > awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\ \).*\n\1.*/s/^\(.*\)\(\n\)\(.*\ \)\(.*\)/\1 \4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容八:sed 使用 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数第案例

8.1 sed 的工作原理

1) 从第 1 行开始 1 行 1 行地读取文件里的内容
2) 每读取 1 行就将内容存入到 pattern space (模型空间) 里面,pattern space (模型空间) 里的内容默认会自动显示出来
3) 在 pattern space (模型空间) 中执行 sed 命令
4) 再显示 pattern space (模型空间) 中的内容然后将其清空
5) 之后重复以上操作再开始读取文件里的下 1 行
6) pattern space (模型空间) 里的内容可以存储到 hold space (保持空间) 里面


补充:
1) pattern space (模型空间) 相当于处理内容的流水线
2) hold space (保持空间) 相当于暂时存储内容的仓库

8.2 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的讲解

8.2.1 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的作用简介

1) g 将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除
2) G 将 hold space (保持空间) 中的内容 append (添加) 到 pattern space (模型空间) 分行符 “/n” 后
3) h 将 pattern space (模型空间) 中的内容拷贝到 hold space (保持空间) 中,原来的 hold space (保持空间)里的内容被清除
4) H 将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后
5) d 删除 pattern space (模型空间) 中的所有行,并执行下 1 行
6) D 删除 pattern space (模型空间) 中的第 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行

8.2.2 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的功能示意图
P H     P H     P H
1    h  1 1  d    1

P H     P H     P H     P H
2 1  G  2 1  H  2 1  d    1
        1       1 2       2
                  1       1

P H     P H     P H
3 2  G  3 2  h  3 3
  1     2 1     2 2
        1       1 1

P H     P H
3 2  g  2 2
  1     1 1


补充:
1) 这里的 P 指的是 pattern space (模型空间)
2) 这里的 H 指的是 hold space (保持空间)
3) h 其实就是清空现在粘贴板里的内容然后重新复制
4) H 其实就是不清空现在粘贴板里的内容然后再再原来粘贴版的内容基础上再添加复制
5) g 其实就是替换粘贴
6) G 其实就是添加粘贴

8.3 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的使用案例

8.3.1 将第 1 行的数据添加到第 3 行后面
# sed -e '1h' -e '3G' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.2 将第 1 行的数据替换第 3 行
# sed -e '1h' -e '3g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.3 将第 1 行和第 2 行的数据添加到第 3 行后面
# sed -e '1h' -e '2H' -e '3G' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.4 将第 1 行和第 2 行的数据替换第 3 行
# sed -e '1h' -e '2H' -e '3g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.5 将第 1 个以 a 开头的行的数据添加到第 3 行后面
# sed -e '/^a/h' -e '3G' 1.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.6 将第 1 个和第 2 个以 a 开头的行的数据添加到第 3 行后面
# sed -e '/^a/h' -e'/^a/H' -e '3G' 1.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.7 将 eternalcenter 的 eternal 和 center 以空格分开
# echo "eternalcenter" | sed -r 'H;s/(.{7}).*/\1/;x;s/.*(.{6})$/\1/;x;G;s/\n/ /'

(
1) 执行 H 参数,将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 s 参数
2) 执行 s/(.{7})./\1/,将 pattern space (模型空间) 中的内容只保留前 7 个字符,并将此时的 pattern space (模型空间) 传输给 x 参数 3) 执行 x 参数,将 hold space (保持空间) 和 pattern space (模型空间) 内容互换,,并将此时的 pattern space (模型空间) 传输给 s 参数 4) 执行 s/.(.{6})$/\1/,将 pattern space (模型空间) 中的内容只保留后 6 个字符,并将此时的 pattern space (模型空间) 传输给 x 参数
5) 执行 x 参数,将 hold space (保持空间) 和 pattern space (模型空间) 内容互换,,并将此时的 pattern space (模型空间) 传输给 G 参数
6) 执行 G 参数,将 hold space (保持空间) 中的内容 append (添加) 到 pattern space (模型空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 s 参数
7) 执行 s/(.{7}).*/\1/,将 pattern space (模型空间) 中的内容删除换行符 “\n”
)

8.3.8 显示九九乘法表
# seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'


补充:
1) 执行 seq 9 命令,第 1 行输出 1,第 2 行输出 2,以此类推直到第 9 行,并将结果传输给 sed 命令
2) 执行 sed 的 H 参数,将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 g 参数。当执行到第 1 行时,添加到 hold space (保持空间) 是 1,hold space (保持空间) 里的值是 \n1,当时执行到第 2 行时,添加到 hold space (保持空间) 是 2,hold space (保持空间) 里的值是 \n1\n2,依次类推
3) 执行 sed 的 g 参数,将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除。当执行到第 1 行时,hold space (保持空间)里的值是 \n1,pattern space (模型空间) 里的值将是 \n1,显示的结果也会是 1,当执行到第 2 行时,hold space (保持空间)里的值是 \n1\n2,pattern space (模型空间) 里的值将是 \n1\n2,显示的结果也会是 \n1\n2,依次类推,并将结果传输给命令 awk
3) awk,将多行视为 1 行,会执行从 1 到此行列数次循环,变量 i 初始时的值为 1,每执行 1 次则变量 i 到值会加 1,每次循环时会显示:“此时变量 i 的值”*“此行的列数”=“此时变量 i 的值和此行的列数的乘积““当此时变量 i 的值等于此行的列数时则换行,否则的话则显示制表符的空格长度”。当执行到第 1 行时显示的是 1×1=1,当执行到第 2 行时显示的是 1×2=2 2×2=4,以此类推

内容九:sed 一次匹配多个参数的案例

在 abc 和 bbb 那两行后面添加 1 行 eternalcenter

# sed -e "/abc/a eternalcenter" -e "/^bbb/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容十:sed r 参数和 w 参数读取和写入文件

10.1 将 test2.txt 文件里的内容添加到 test1.txt 文件里内容的第3 行后面

# sed '3r test2.txt' test1.txt

(补充:这里的 test1.txt 和 test2.txt 是要被 sed 操作的测试文件)

10.2 将 test1.txt 文件里内容的第三行保存到 test2.txt 中

# sed '3w test2.txt' test1.txt

(补充:这里的 test1.txt 和 test2.txt 是要被 sed 操作的测试文件)

内容十一:sed q 参数退出当前执行的内容

11.1 执行完第 5 行后退出

# sed '5 q' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

11.2 执行完包含 eternalcenter 的行后退出

# sed '/eternalcenter/ q' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

11.3 执行完第 3 行后退出,并将退出代码设置为 3

# sed '3 q 3' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容十二:sed b 参数和 t 参数设置标签分支

12.1 如果某行不包含 eternalcenter 则将此行中内容的 0 换成 1,否则将此行中内容的 0 换成 2

# sed '{/eternalcenter/b lable;s/0/1/;:lable}' test.txt


补充:
1) 在这里 lable 是标签
2) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed '/eternalcenter/ba;s/0/1/;b;:a' test.txt


补充:
1) 在这里 a 是标签
2) 这里的 test.txt 是要被 sed 操作的测试文件

12.2 如果某行包含 eternalcenter 则将此行中内容的 0 换成 1,否则将此行中内容的 0 换成 2

# sed '/eternalcenter/s/0/1/;t;s/0/2/' test.txt


补充:
1) 在这里的标签省略了,所以标签分支会一直到头部
2) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed -e :a -e '/eternalcenter/s/0/1/;ta;s/0/2/' test.txt


补充:
1) 在这里 a 是标签
2) 这里的 test.txt 是要被 sed 操作的测试文件

[命令] Linux 命令 grep (显示文本的行)

案例一:grep 使用 perl 正则表达式匹配

# grep -P '^root' /etc/passwd

或者:

# grep --perl-regexp '^root' /etc/passwd

(补充:这里以匹配 /etc/passwd 文件中开头为 root 的行为例)

案例二:grep 使用正则表达式匹配

# grep -e '^root' /etc/passwd

或者:

# grep --regexp=PATTERN '^root' /etc/passwd

(补充:这里以匹配 /etc/passwd 文件中开头为 root 的行为例)

案例三:grep 使用扩展正则表达式匹配

# grep -E '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# grep --extended-regexp '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行为例)

案例四:grep 显示行号

4.1 显示某些关键字所在行行号

# egrep -n '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --line-number '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --line-buffered '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并显示行号为例)

4.2 显示哪些行号是空行

# egrep -n ^$ /etc/resolv.conf

或者:

# egrep --line-number ^$ /etc/resolv.conf

或者:

# egrep --line-buffered ^$ /etc/resolv.conf

(补充:这里以匹配 /etc/resolv.conf 里的空行并显示行号为例)

案例五:grep 取反匹配

5.1 取反不匹配某些关键字

# egrep -v '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --invert-match '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里不含有 IP 地址的行并显示行号为例)

5.2 取反不显示空行

# egrep -v ^$ /etc/resolve.conf

或者:

# egrep --invert-match ^$ /etc/resolve.conf

(补充:这里以匹配 /etc/resolve.conf 文件里不为空的行为例)

5.3 取反不显示以井号 “#” 开头的行

# grep '^[^#]' /etc/resolve.conf

(补充:这里以匹配 /etc/resolve.conf 文件里不以井号 “#” 开头的行为例)

5.4 取反不显示以井号 “#” 或空格 “ ” 开头的行

# grep '^[^#| ]' /etc/resolve.conf

(补充:这里以匹配 /etc/resolve.conf 文件里不以井号 “#” 和空格 “ ” 开头的行为例)

案例六:grep 只显示匹配的部分

# egrep -o '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --only-matching '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 里的 IP 地址为例)

(注意:这里匹配后只显示 IP 地址,而不显示 IP 地址所在行里的其他内容)

案例七:grep 匹配时忽略大小写

# egrep -i '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --ignore-case '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并忽略大小写为例)

案例八:grep 统计匹配成功次数

# egrep -c '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --count '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行的数量为例)

案例九:grep 将匹配成功的部分自动添加颜色

# egrep --color=auto '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并自动添加颜色为例)

案例十:grep 将匹配成功的部分自动取消颜色

# egrep --color=no '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192  

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并取消自动添加颜色为例)

案例十一:grep 一次匹配多个参数

# grep -e root -e zhumingyu /etc/passwd

(补充:这里以匹配 /etc/passwd 文件中包含 root 或 zhumingyu 的行为例)

案例十二:grep 显示匹配内容附近的内容

12.1 显示匹配内容的前 10 行

# grep -a 10 eternalcenter test.txt

或者:

# grep --text 10 eternalcenter test.txt

(补充:这里以匹配 test.txt 文件中包含 eternalcenter 的行并显示其前 10 行为例)

12.2 显示匹配内容的后 10 行

# grep -b 10 eternalcenter test.txt

或者:

# grep --byte-offset 10 eternalcenter test.txt

(补充:这里以匹配 test.txt 文件中包含 eternalcenter 的行并显示其后 10 行为例)

12.3 显示匹配内容的前后 10 行

# grep -a -b 10 eternalcenter test.txt

或者:

# grep --text --byte-offset 10 eternalcenter test.txt

(补充:这里以匹配 test.txt 文件中包含 eternalcenter 的行并显示其前后 10 行为例)

案例十三:grep 安静匹配

# grep -q root /etc/passwd

或者:

# grep --quiet root /etc/passwd

或者:

# grep --silent root /etc/passwd
# echo $?
0


补充:
1) 这种方法主要用于在 Shell 脚本中,随后使用 if 判断判断匹配是否成功
2) 这里以匹配 /etc/passwd 中是否包含 root 用户为例

[排错] 解决 Linux 删除数据后依旧无法释放空间

解决方法

步骤一:理解出现问题的原因

当删除某些数据时,此数据正在被某个进程使用,则这些数据需要 kill 掉这个进程后才能被删除掉

步骤二:显示目录的使用状态

# df -h

(注意:如果出现已用存储加剩余存储小于总存储的情况,就代表出现了被删除的数据正在被进程使用的情况 )

步骤三:显示对应目录里所有一级子目录的大小

# du -ah --max-depth=1

步骤四:显示是否有进程在使用正在被删除的数据

# lsof | grep delete

步骤五:杀死这些进程以让这些数据被成功删除

# kill -9 `lsof | grep delete | awk '{print $2}'` 

(注意:kill 进程有一定的风险,请小心执行)

[实验] Linux 硬盘的加密 (通过 crypt 实现)

纪念:站主于 2019 年 11 月完成了此开源实验,并将过程中的所有命令经过整理和注释以后,形成以下教程

步骤一:硬盘加密后的注意事项

1) 加密后不能直接挂载
2) 加密后硬盘丢失也不用担心数据被盗
3) 加密后必须做映射才能挂载

步骤二:生成一个新的分区

2.1 显示现有的分区

# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sr0     11:0    1 1024M  0 rom  
vda    253:0    0   10G  0 disk 
└─vda1 253:1    0   10G  0 part /
vdb    253:16   0   10G  0 disk 

(补充:在这里是加了一个 vdb 硬盘用来进行分区并加密)

2.2 创建一个新的分区

# fdisk /dev/vdb
命令(输入 m 获取帮助):n
分区号 (1-8,默认 1):
起始 扇区 (0-20971440,默认 0):
Last 扇区 or +扇区 or +size{K,M,G,T,P} (0-20971440,默认 20971440):+5G
分区 1 已设置为 Linux native 类型,大小设为 5 GiB

命令(输入 m 获取帮助):w
The partition table has been altered!

2.2 显示新的分区

# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sr0     11:0    1 1024M  0 rom  
vda    253:0    0   10G  0 disk 
└─vda1 253:1    0   10G  0 part /
vdb    253:16   0   10G  0 disk 
└─vdb1 253:17   0    5G  0 part 

步骤三:创建逻辑卷

3.1 创建卷组

# vgcreate mysqldatavg /dev/vdb1
WARNING: sun signature detected on /dev/vdb1 at offset 508. Wipe it? [y/n]: y
  Wiping sun signature on /dev/vdb1.
  Physical volume "/dev/vdb1" successfully created.
  Volume group "mysqldatavg" successfully created

3.2 创建逻辑卷

# lvcreate -n mysqldatalv -L 1G mysqldatavg
  Logical volume "mysqldatalv" created.

步骤四:给逻辑卷加密

4.1 给逻辑卷加密

# cryptsetup luksFormat /dev/mapper/mysqldatavg-mysqldatalv

WARNING!
========
这将覆盖 /dev/mapper/mysqldatavg-mysqldatalv 上的数据,该动作不可取消。

Are you sure? (Type uppercase yes): YES
输入 /dev/mapper/mysqldatavg-mysqldatalv 的口令:
确认密码:

4.2 解锁逻辑卷

# cryptsetup luksOpen /dev/mapper/mysqldatavg-mysqldatalv mysqldata
输入 /dev/mapper/mysqldatavg-mysqldatalv 的口令:

(补充:这里的 mysqldata 是解锁后的硬件名称)

4.3 格式化逻辑卷

# mkfs.ext4 /dev/mapper/mysqldata

(注意:要先解锁了逻辑卷以后才能格式化逻辑卷)

4.4 锁住逻辑卷

# cryptsetup luksClose mysqldata

内容五:自动挂载加密逻辑卷

5.1 修改系统自动挂载文件

# vim /etc/fstab

添加以下内容:

......
/dev/mapper/mysqldata /var/lib/mysql ext4 defaults 0 0

5.2 创建一个映射器

# vim /etc/cypttab

添加以下内容:

......
mysqldata /dev/mapper/mysqldatavg-mysqldatalv /root/keyfile luks

(补充:这里的三个参数分别代表:虚拟设备名、真实设备、密码的存储文件)

5.3 创建随机密钥文件

# dd if=/dev/urandom of=/root/keyfile bs=1024 count=4

5.4 将密钥文件设为只读

# chmod 0400 /root/keyfile

5.5 将密码添到 luks 中,让密码立刻生效

# cryptsetup luksAddKey /dev/mysqldatavg/mysqldatalv /root/keyfile
输入任意已存在的口令:

5.6 测试挂载效果

5.6.1 挂载加密逻辑卷
# mount -a
5.6.2 测试加密效果
# df -h

[命令] Linux 命令 awk (显示文本的列)

内容一:awk 格式

# awk <option> '<condition>{<command>}' <file>

或者:

# awk <option> ' BEGIN{<option> <command>} {<command>} END{<command>}' <file>


补充:在这里
1) BEGIN{ } 里的指令只执行 1 次,并且是最先执行
2) { } 里的指令会一行行地执行文件里的内容
3) END{ } 里指令只执行 1 次,并且是最后执行

内容二:awk 选项

2.1 awk 常用选项

2.1.1 awk 的 -F 选项

awk 默认是以空格分割列,但是也可以通 -F 参数指定分割每一列的符号

2.1.2 awk 的 -v 选项

将变量导入 awk 中,并可以通过 ‘{print (<variable>)}’ 将变量显示出来

2.2 awk 的 RS、ORS、FS、OFS 选项

2.2.1 awk 的 RS、ORS、FS、OFS 选项作用

1) RS 全名为 Record Separator (输入记录分隔符)
2) ORS 全名为 Output Record Separate (输出记录分隔符)
3) FS 全名为 Field Separator (输入字段分隔符)
4) OFS 全名为 Output Field Separator (输出字段分隔符)

2.2.2 RS、ORS、FS、OFS 的常用值

1) RS=”” 每 1 次读取 1 个段落,默认情况下在 awk 中,把没有空行分割的多行会被定义为 1 个段落
2) RS=”\0″ 一次性读取所有内容,但是前提是 \0 字符不能在文件中存在
3) RS=”^$” 一次性读取所有内容
4) RS=”\n+” 按行读取内容,并且忽略空行

2.3 awk 的 sub、gsub 选项

2.3.1 sub 和 gsub 的区别

1) sub 只匹配第一个字符串,类似于 sed ‘s//’
2) gsub 匹配所有字符串,类似于 sed ‘s//g’

2.3.2 sub 和 gsub 的格式
2.3.2.1 sub 的格式
# sub (<original string or regular expression>, <substitution string>):
# sub (<original string or regular expression>, <substitution string>, <target string>)
2.3.2.2 gsub 的格式
# gsub (<original string or regular expression>, <substitution string>):
# gsub (<original string or regular expression>, <substitution string>, <target string>)

内容三:awk 的常用内置变量

1) $0 文本当前行的全部内容
2) $1 文本的第 1 列
3) $2 文本的第 2 列
4) $3 文本的第 3 列

(补充:“$” 号后面的数字以此类推)

5) NR 文本当前行的行数,也就是目前处于第几行
6) NF 文本当前行的列数,也就是目前一共有几列
7) $(NF-1) 文本倒数第 1 列
8) $(NF-2) 文本倒数第 2 列
9) $(NF-3) 文本倒数第 3 列
10) , 代表空格
11) \t 代表 tab 制表位,就是比一个空格更多的空格
12) FILENAME 当前文件名
13) IGNORECASE 是否忽略大小写,当 IGNORECASE=1 时则忽略大小写

内容四:awk 基础显示的案例

4.1 显示第 1 列和第 3 列,但中间没有空格

# awk '{print $1,$3}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.2 显示第 1 列和第 3 列

# awk '{print $1,$3}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.3 显示第 1 列和第 3 列,但中间有很长的空格空间

# awk '{print $1,$3}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.4 显示倒数第 2 列

# awk '{print $(NF-2)}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.5 显示每 1 行的行号和列数

# awk -F: '{print NR,NF}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.6 显示前 1 个命令结果的第 5 列

# df -h | awk '{print $5}'

4.7 显示前 1 个命令最后 1 行结果的第 5 列

# df -h / | tail -1 | awk '{print $5}'

或者:

# df -h / | awk 'END{print $5}'

4.8 显示第 2 列到最后 1 列

# cat test.txt | awk '{$1="";print}' | sed -r 's/( )(.*)/\2/'


补充:
1)这里的 /etc/passwd 是要被 awk 操作的测试文件
2)这里的 $1=”” 是将第 1 列的内容变为空
3)这里的 print 作用相当于 print $0

4.9 显示每行的行数、列数、第 1 列和最后 1 列,并且将冒号 “:” 视为分割符

# awk -F: '{print NR,NF,$1,$NF}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.10 显示总行数减 1 的值

# awk 'END{print --NR}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.11 显示总行数加 1 的值

# awk 'END{print ++NR}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.12 显示最后 1 行第 1 列字符的长度

# echo eternalcenter | awk 'END{print length($1)}'

4.13 显示第 1 列,并且将冒号 “:” 视为分割符

# awk -F: '{print "\047 $1 \047"}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

(注意:这里的 \047 代表的是单引号 “’”)

4.14 显示第 2 列的第 1 到第 3 个字符

# awk '{print substr($2,1,3)}' test.txt

或者:

# awk '{$a=substr($2,1,3);print $a;}'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

(注意:在 awk 当中字符索引 substr 都是从第 1 个开始的,不是从第 0 个开始的)

4.15 显示第 2 列的第 1 到第最后 1 个字符

# awk '{print substr($2,1,)}' test.txt

4.16 将第 1 列以横杠 “-” 作为分隔符进行拆分,并将拆分的内容一行行地显示

# echo eternal-center-zmy | awk '{split ($0,a,"-");for (i in a) print a[i]}'

4.17 自定义列并显示第 2 列 (不包含多余的空格)

# awk 'BEGIN{FIELDWIDTHS="2 2:4 2:3"}{print $2}' test.txt


补充:
1) FIELDWIDTH 函数的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 2:4 2:3″ 代表第 1 列有 2 个字符,相隔 2 个字符后第 2 列开始有 4 个字符,再相隔 2 个字符后第 3 列开始有 3 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

或者:

# awk -vFIELDWIDTHS="2 2:4 2:3" '{print $2}' test.txt


补充:
1) FIELDWIDTH 的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 2:4 2:3″ 代表第 1 列有 2 个字符,相隔 2 个字符后第 2 列开始有 4 个字符,再相隔 2 个字符后第 3 列开始有 3 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

4.18 自定义列并显示第 2 列 (包含多余的空格)

# awk 'BEGIN{FIELDWIDTHS="2 6 5"}{print $2}' test.txt


补充:
1) FIELDWIDTH 函数的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 6 5″ 代表第 1 列有 2 个字符,第 2 列开始有 6 个字符,第 3 列开始有 5 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

或者:

# awk -vFIELDWIDTHS="2 6 5" '{print $2}' test.txt


补充:
1) FIELDWIDTH 函数的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 6 5″ 代表第 1 列有 2 个字符,第 2 列开始有 6 个字符,第 3 列开始有 5 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

4.19 自定义每 1 列之间的间距 (保留原来的行结构)

# awk '{ printf "%-9s %-8s %-8s\n", $1, $2, $3 }' test.txt


补充:
1) 这里以设置第 1 列和第 2 列之间间隔 9 个空格,第 2 列和第 3 列之间间隔 8 个空格为例
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.20 自定义每 1 列之间的间距 (所有行合为 1 行)

# awk '{ printf "%-4s %-8s %-6s", $1, $2, $3 }' test.txt


补充:
1) 这里以设置第 1 列和第 2 列之间间隔 9 个空格,第 2 列和第 3 列之间间隔 8 个空格,且每行之间间隔 6 个空格为例
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.21 将冒号 “:” 视为分割符

# awk -F: '{print $1,$7}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.22 将冒号 “:” 和句号 “.” 视为分割符

# awk -F [:.] '{print $1,$10}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.23 将逗号 “,” 作为分隔符但是处于两个双引号 “””” 之间的逗号 “,” 不作为分隔符,并显示其中的第 2 列 (可以将包含特定符号的多列示为 1 列)

# awk 'BEGIN{FPAT="[^,]+|\".*\""}{print $2}' test.txt


补充:
1) FPAT 函数的作用是:使用正则表达式进行匹配,将匹配成功的部分作为列
3) 这里以不包含逗号 “,” 和两个双引号 “” 包括两个双引号 “” 里的内容作为列
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.24 当第 2 列出现多个相同的内容时只显示第 1 次出现的行 (也就是以第 2 列为基准进行去重)

# awk '{arr[$2]=arr[$2]+1;if(arr[$2]==1){print}}' test.txt

或者:

# awk '{arr[$2]++;if(arr[$2]==1){print}}' test.txt

或者:

# awk '{++arr[$2];if(arr[$2]==1){print}}' test.txt

或者:

# awk '!arr[$2]++{print}' test.txt


补充:
1) 当把 arr[$2] 放到大括号 “{}” 外面时它自身就是 1 个判断,当 arr[$2] 第 1 次遭遇某个内容时,arr[$2] 此时的值是 0 ,然后才进行 ++ (也就是加 1),而当 arr[$2] 的值是 0 时,给他取反的 !arr[$2] 就是布尔真,而布尔为真时就执行 {print}
2) 当后来 arr[$2] 不是第 1 次遭遇某个内容时,arr[$2] 的值就不是 0 了,而给他取反的 !arr[$2] 就是布尔假了,也就不会再执行后面的 {print}
3) 以此类推
4) 这里的 test.txt 是要被 awk 操作的测试文件

4.25 显示所有非空的行

# ifconfig | awk 'BEGIN{RS=""}{print}'

4.26 显示第 1 个段落

# ifconfig | awk 'BEGIN{RS=""}NR==1{print}'

4.27 显示 IP 地址

# ifconfig | awk '/inet/&&!($2~/^127/){print $2}'

或者:

# ifconfig | awk 'BEGIN{RS=""};!/127.0.0.1/{print $6}'

或者:

# ifconfig | awk 'BEGIN{RS="";FS="\n"}!/127.0.0.1/{$0=$2;FS=" ";$0=$0;print $2}'


补充:
1) 执行 RS=”” 每次读取 1 个段落
2) 执行 !/lo/ 不读取带 lo 的段落
3) 执行 FS=”\n” 将换行符 “\n” 设置为分隔符
4) 执行 !/127.0.0.1/ 只选择不带 127.0.0.1 的段落
5) 执行 $0=$2 将此段落里的内容换成第 2 行的内容 ($0 代表所有内容),设置了 FS 之后,需要重新给 $0 赋值以后才能让 FS 的新值生效
6) 执行 FS=” ” 将空格 “ ” 设置为分隔符
7) 执行 $0=$0 让所有内容等于所有内容 ($0 代表所有内容),设置了 FS 之后,需要重新给 $0 赋值以后才能让 FS 的新值生效
8) 执行 print $2 打印第 2 列

或者:

# ip a s | awk '/[1-2]?[0-9]{0,2}\.[1-2]?[0-9]{0,2}/&&!/127.0.0.1/{print $2}'

4.28 显示从包含 1.eternalcenter.com 的行到包含 5.eternalcenter.com 的行的所有内容

# awk '~/1.eternalcenter.com/,~/5.eternalcenter.com/' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.29 显示正数从 [mysql] 的行开始到下 1 个中括号 “[]” 出现前出现的所有内容

# vim awk.txt

创建以下内容:

index($0, "[mysql]"){
    print
    while( (getline var) > 0 ){
        if(var ~ /\[.*\]/){exit}
        print var
    }
}
# awk -f awk.txt /etc/my.cnf


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 执行 index($0, “[mysql]”),搜索 $0 中内容包含 [mysql] 的行 ($0 代表所有内容)
3) 执行 print 将匹配到的这 1 行显示出来
4) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第一行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
5) 执行 while( (getline var) > 0 ){print var},从 [mysql] 这 1 行往下第 1 行开始往下 1 行 1 行地读取内容,并形成循环,每读取有 1 行,就将内容赋值给变量 var,且 getline 的返回值就为 1,确定 (getline var) 的返回值大于 0 时将 var 的内容显示出来
6) 执行 if(var ~ /[.*]/){exit} 如果匹配到变量 var 里的内容包含中括号了 “[]” 则显示出来
7) 这里的 /etc/my.cnf 是要被 awk 操作的测试文件

或者:

# echo '[mysql]' ; awk 'index($0, "[mysql]"){while((getline var)>0){if(var ~/\[.*\]/){exit} print var}}' /etc/my.cnf


补充:
1) 执行 echo ‘[mysql]’ 显示 echo ‘[mysql]’
2) 执行 index($0, “[mysql]”),搜索 $0 中内容包含 [mysql] 的行 ($0 代表所有内容)
3) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第一行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
4) 执行 while( (getline var) > 0 ){print var},从 [mysql] 这 1 行往下第 1 行开始往下 1 行 1 行地读取内容,并形成循环,每读取有 1 行,就将内容赋值给变量 var,且 getline 的返回值就为 1,确定 (getline var) 的返回值大于 0 时将 var 的内容显示出来
5) 执行 if(var ~ /[.*]/){exit} 如果匹配到变量 var 里的内容包含中括号了 “[]” 则显示出来
6) 这里的 /etc/my.cnf 是要被 awk 操作的测试文件

4.30 将上斜号加星号 “/*” 和星号加上斜号 */ 里的内容视为注释,并省略掉里面的内容显示其他内容

# vim awk.txt

创建以下内容:

index($0, "/*"){
    #if in the same line with */
    if (index ($0,"*/")){
        print gensub("^(.*)\\/\\*.*\\*\\/(.*)$","\\1\\2","g",$0)
     }
 
    else {

    #if not in the same line with  */

    #display the content before /*
    print gensub("^(.*)/\\*.*","\\1","g",$0)

    #alaways read until encountered */
        while( (getline var) > 0 ){
            if(index(var,"*/")){
                print gensub("^.*\\*/(.*)","\\1","g",var)
                break
            }
        }
    }
}

!index($0, "/*"){
    print
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) gensub 函数的格式是:gensub(“”,””,”g”,),作用是:将字符串中的某些字符进行替换
4) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第一行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
5) 执行 while( (getline var) > 0 ){print var},从 [mysql] 这 1 行往下第 1 行开始往下 1 行 1 行地读取内容,并形成循环,每读取有 1 行,就将内容赋值给变量 var,且 getline 的返回值就为 1,确定 (getline var) 的返回值大于 0 时将 var 的内容显示出来

4.31 将 2 段分别包含 i-order 内容和 fail 内容并以中括号 “[” 结尾的段落视为 1 段并显示

# vim awk.txt

添加以下内容:

BEGIN{
    RS="]\n"
    ORS=RS
}

{
    if($0 ~ /ok/ && prev ~ /order/){
        print prev
        print $0
    }
    prev = $0
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.32 显示被执行的文件 test.txt

# awk '{print FILENAME}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容五:awk 替换显示的案例

5.1 将第 1 列为 root 的行的第 3 列替换成 “1” 后显示,并且将冒号 “:” 视为分割符

# awk -F: '$1=="root"{ $3 = '1';print  }' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

5.2 将第 4 列里的横杠 “-” 替换成空值后显示

# echo "a- b c 2022-01-01 a:d" | awk 'gsub(/-/,"",$4)'

5.3 统计第 4 列里的横杠 “-” 的次数并把第 4 列替换统计后的次数后显示

# echo "z m 7 2022-01-01 a:d" | awk '$4=gsub(/-/,"",$4)'

5.4 统计此行中横杠 “-” 出现的次数并将第 4 列替换统计后的次数后显示

# echo "z- m y 2021-11-11 z:y" | awk '$4=gsub(/-/,"")'
z m y 3 z:y

5.5 将全部横杠 “-” 替换成空值后显示

# echo "z m y z-m-y 2022-01-01 z:y" | awk 'gsub(/-/,"")'
z m y zmy 20211111 z:y

或者:

# echo eternal-center-zmy | awk 'BEGIN{ FS="-";}{print $1,$2,$3}'
eternal center zmy

或者:

# echo eternal-center-zmy | awk '{split ($0,a,"-");print a[1],a[2],a[3]}'
eternal center zmy

5.6 将全部横杠 “-” 和冒号 “:” 替换成空值后显示

# echo "a- b c 2022-01-01 a:d" | awk '{gsub(/-|:/, "");print}'

5.7 将全部横杠 “-” 替换成 3 个星号 “***” 后显示

# echo eternal-center-zmy | awk 'BEGIN{ FS="-";OFS="***"}{print $1,$2,$3}'
eternal***center***zmy

5.8 给每 1 行前面添加行号后显示

# cat test.txt 
eternalcenter.com-ec-myz
eternalcentre.com-ec-myz
eternalcenter.org-ec-myz
eternalcentre.org-ec-myz

# awk 'BEGIN{ OFS="." }{ print NR,$0 }' test.txt 
1.eternalcenter.com-ec-myz
2.eternalcentre.com-ec-myz
3.eternalcenter.org-ec-myz
4.eternalcentre.org-ec-myz

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.9 在 a b c d 中的 b 后面插入 e f g

# echo "a b c d" | awk '{$2=$2" e f g";print}'

(补充:修改字段或 NF 的值后 $0 会进行重建并进行联动效应)

5.10 去除字符串中多余的空格,每个字节只用 1 个空格隔开

# echo "a     b c        d" | awk '{$2=$2;print}'

(补充:OFS 默认等于一个空格 “ ”,这里修改了 $2 的值以后, awk 会根据 OFS 的值重建 $0 ($0 代表所有内容),它会去使用 OFS 的值去链接各个字段)

5.11 去除字符串中多余的空格,每个字节使用多个个空格隔开

# echo "a     b c        d" | awk 'BEGIN{OFS="\t"}{$1=$1;print}'

(补充:OFS 默认等于 1 个空格 “ ”,在这里先试将 OFS 的值修改成了 “\t”,再修改了 $2 的值以,此时 awk 会根据 OFS 的值重建 $0 ($0 代表所有内容),它会去使用 OFS 的值去链接各个字段)

5.12 以竖杠 “|” 作为分割符将第 2 列的数字去掉后显示

# cat test.txt 
eternalcenter.com|201901ec|com
eternalcentre.com|201902ec|com
zhumingyu.com|201903zmy|com
mingyuzhu.com|201904myz|com
eternalcenter.org|201905ec|org
eternalcentre.org|201906ec|org


# awk -F '|' 'BEGIN{ OFS="|" }{sub(/[0-9]+/,"",$2);print$0}' test.txt 
eternalcenter.com|ec|com
eternalcentre.com|ec|com
zhumingyu.com|zmy|com
mingyuzhu.com|myz|com
eternalcenter.org|ec|org
eternalcentre.org|ec|org

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

或者:

# cat test.txt 
eternalcenter.com|201901ec|com
eternalcentre.com|201902ec|com
zhumingyu.com|201903zmy|com
mingyuzhu.com|201904myz|com
eternalcenter.org|201905ec|org
eternalcentre.org|201906ec|org

# awk -F '|' -v OFS='|' '{sub(/[0-9]+/,"",$2);print $0}' test.txt 
eternalcenter.com|ec|com
eternalcentre.com|ec|com
zhumingyu.com|zmy|com
mingyuzhu.com|myz|com
eternalcenter.org|ec|org
eternalcentre.org|ec|org

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.13 将句号 “.” 替换成换行符后显示

# cat test.txt 
m.y.z

# awk 'BEGIN{RS=".";}{print $0}' test.txt 
m
y
z

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.14 将换行符替换成句号 “.” 后显示

# cat test.txt 
m
y
z


# awk 'BEGIN{ORS=".";}{print $0}' test.txt 
m.y.z.

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.15 awk 行列转换 (每 1 行最前面有 1 个空格)

# awk '{for(i=1;i<=NF;i++){arr[i]=arr[i]" "$i}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.16 awk 行列转换

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.17 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (多个第 1 列相同的行,后面的行会在去除第 1 列后将剩余的列加在上 1 行的后面)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.....\).*\n\1.*/s/^\(.*\)\(\n\)\(.....\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.18 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以 .com 结尾)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\.com\).*\n\1.*/s/^\(.*\)\(\n\)\(.*\.com\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.19 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以空格 “ ” 结尾)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\ \).*\n\1.*/s/^\(.*\)\(\n\)\(.*\ \)\(.*\)/\1 \4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容六:awk 导入变量的案例

6.1 将 date 命令的运行结果,通过 getline 赋值给 d ,之后将 d 的值显示出来

# awk 'BEGIN {"date"|getline d; print d}'

6.2 将 eternalcenter 的值赋予给变量 content,并将变量 content 的值显示出来

# content=eternalcenter
# awk '{print "$content"}'
content
# awk '{print '$content'}'
eternalcenter

(注意:awk 在使用变量时必须使用单引号 “””,否则会把变量名称当作字符串)

6.3 将 eternalcenter 的值赋予变量 value,再将变量 value 的值赋予给变量 content,并将变量 content 的值显示出来

# value=eternalcenter
# echo | awk -v content=$value '{print(content)}'
eternalcenter

6.4 将 mingyuzhu 的值赋予变量 n,显示第 1 列等于变量 n 的值的行第 1 列、第 2 列和第 3 列

# gawk -F: '($1 == n) {print $1,$2,$3}' n=mingyuzhu /etc/group
(补充:这里的 /etc/group 是要被 gawk 操作的测试文件)

内容七:awk 变量判断的案例

如果变量 value 的值为 eternalcenter 则显示 0 否则显示 1

# echo eternalcenter | awk -v value="eternalcenter" '{print($1==value)? "0":"1"}'

或者:

# echo eternalcenter | awk -v value="eternalcenter" 'BEGIN{if($1==value){result=0}else(result=1)} END{print result}'

内容八:awk 常量显示的案例

# awk -F: '{print $1,"interpreter:",$7}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

内容九:awk 匹配显示的案例

9.1 显示包含 Failed 的行的第 11 列

# awk '/Failed/{print $11}' /var/log/secure

或者:

# awk 'index($0,"Failed"){print $11}' /var/log/secure

(补充:这里的 /var/log/secure 是要被 awk 操作的测试文件)

9.2 显示以 bash 结尾的行,并且将冒号 “:” 视为分割符

# awk -F: '/bash$/{print}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.3 显示以 root 或者 adm 开头的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '/^(root|adm)/{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.4 显示以 r 或者 a 开头的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '/^[ra]/{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.5 显示第 1 列包含 root 的行,并且将冒号 “:” 视为分割符

# awk -F: '$1~/root/' /etc/passwd

或者:

# awk -F: '$1 ~ /root/' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.6 显示第 7 列不以 nologin 结尾的行的第 1 列和第 7 列,并且将冒号 “:” 视为分割符

# awk -F: '$7!~/nologin$/{print $1,$7}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.7 显示前 1 个命令包含 Rx p 的行的第 5 列

# ifconfig eth0 | awk '/RX p/{print $5}'

9.8 显示前 1 个命令以 / 结尾的行的第 4 列

# df -h | awk '/\/$/{print $4}'

9.9 显示第 1 列以两个数字结尾的行的第 1 列,并且将冒号 “:” 视为分割符

# awk -F: '$1 ~/[0-9][0-9]$/(print $1}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.10 显示以 root 开头到以 mysql 开头的所有行的第 1 列,并且将冒号 “:” 视为分割符

# awk -F '/^root/,/^mysql/{print $1}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

内容十:awk 使用比较符号匹配的案例

10.1 显示行号等于 3 的行,并且将冒号 “:” 视为分割符

# awk -F: 'NR==3{print}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.2 显示第 3 列数值等于 1000 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3==1000{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.3 显示第 3 列数值不等于 1000 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3!=1000{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.4 显示第 3 列数值大于 1000 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3>=1000{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.5 显示第 3 列数值小于 10 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3<10{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.6 显示第 3 列数值大于 10 并且小于 20 的行,并且将冒号 “:” 视为分割符

# awk -F: '$3>10 && $3<20' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.7 显示第 3 列数值大于 1000 或者小于 10 的行,并且将冒号 “:” 视为分割符

# awk -F: '$3>1000 || $3<10' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.8 显示第 3 列乘以第 4 列小于 100 的行,并且将冒号 “:” 视为分割符

# awk -F: '$3 * $4 <100' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.9 显示第 1 列为 root 的行,并且将冒号 “:” 视为分割符

# awk -F: '$1=="root"' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.10 显示第 1 列是 test1 且第 2 列是 test2 的行

# awk '{if($1=="test1"&&$2=="test2"){print}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

10.11 显示第 1 列包含 test1 且第 2 列包含 test2 的行

# awk '$1~/test1/&&$2~/test2/' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

10.12 显示第 1 列是 test1 或者第 2 列是 test2 的行

# awk '{if($1=="test1"||$2=="test2"){print}}' test.txt

或者:

# awk '{if($1=="test1"){print};if($2=="test2"){print}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

10.13 显示第 1 列包含 test1 或者第 2 列包含 test2 的行

# awk '$1~/test1/;$2~/test2/' test.txt

或者:

# awk '$1~/test1/||$2~/test2/' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容十一:awk 计算的案例

11.1 0 + 1

# awk 'BEGIN{print x+1}'

或者:

# awk 'BEGIN{x++;print x}'

(注意:这里的 x 可以不定义,直接用,默认值为 0)

11.2 8 + 2

# awk 'BEGIN{x=8;print x+=2}'

(注意:这里的 x 可以不定义,直接用,默认值为 0)

11.3 8 – 1

# awk 'BEGIN{x=8;x--;print x}'

(注意:这里的 x 可以不定义,直接用,默认值为 0)

11.4 2 + 3

# awk 'BEGIN{print 2+3}'

11.5 3.2 + 3.5

# awk 'BEGIN{print 3.2+3.5}'

11.6 2 * 3

# awk 'BEGIN{print 2*3}'

11.7 2 乘 3 再加 3

# awk 'BEGIN{print 2*3+3}'

11.8 3 加 3 再乘 2

# awk 'BEGIN{print (3+3)*2}'

11.9 求 23 除 8 的余数

# awk 'BEGIN{print 23%8}'

11.10 显示第 3 加上 10 的结果,并且将冒号 “:” 视为分割符

# awk -F: '$3 + 10' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

11.11 找 200 以内 3 的倍数

# seq 200 | awk '$1%3==0'

11.12 找 100 以内 7 的倍数或者包含 7 的数

# seq 100 | awk '$1%7==0||$1~/7/'

11.13 将第 4 列求和后显示,并且将冒号 “:” 视为分割符

# awk -F: 'BEGIN{total=0}{total+=$4}END{print total}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

11.14 当第 2 列中,以横杠 “-” 作为分隔符的第 2 列的内容等于 01,则将此行的第 3 列的数值相加,并以第 1 列作为基准进行显示 (统计员工 1 月份的消费之和)

# cat test.txt 
Tom 2022-01-01 100
John 2022-02-05 300
Tom 2022-03-20 400
John 2022-01-22 200
Tom 2022-01-05 500
# awk '{split($2,a,"-");if(a[2]==01){b[$1]+=$3}}END{for(i in b)print i,b[i]}' test.txt

内容十二:awk 计数的案例

12.1 计算以 bash 结尾的行数

# awk 'BEGIN{x=0}/bash$/{x++} END{print x}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.2 计算第 3 列小于等于 1000 的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($3<=1000){i++}}END{print i}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.3 计算 3 列大于 1000 的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($3>1000){i++}}END{print i}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.4 计算第 7 列以 bash 结尾的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($7~/bash$/){i++}}END{print i}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.5 计算第 3 列数值和第 4 列数值的和并赋值给变量,再将变量的值显示出来,并且将冒号 “:” 视为分割符

# awk -F: '{result=$3+$4; printf result}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.6 计算第 3 列数值除以第 4 列数值再乘以 100.0

# awk '{print $3/$2 * 100.0}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.7 计算第 1 列所有值各自总共出现的次数

# awk '{a[$1]++}END{for(i in a){print i,a[$i]}}' /usr/local/nginx/logs/access.log

(补充:这里的 /usr/local/nginx/logs/access.log 是要被 awk 操作的测试文件)

12.8 统计第 4 列里的横杠 “-” 的次数并把第 4 列替换统计后的次数后显示

# echo "z m 7 2022-01-01 a:d" | awk '$4=gsub(/-/,"",$4)'

12.9 统计此行中横杠 “-” 出现的次数并将第 4 列替换统计后的次数后显示

# echo "z- m y 2021-11-11 z:y" | awk '$4=gsub(/-/,"")'
z m y 3 z:y

案例十三:awk if 判断语句计数的案例

13.1 计算第 3 列小于等于 1000 的行数和第 3 列大于 1000 的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($3<=1000){i++}else{j++}} END{print i,j}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.2 计算第 7 列以 bash 结尾的行数和第 7 列不以 bash 结尾的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($7~/bash$/){i++}else{j++}} END{print i,j}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.3 如果第 3 列等于 0 则显示 “root”,否则显示 “not root”,并且将冒号 “:” 视为分割符

# awk -F: '{print ($3=0 ? "root": not root)}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.4 如果第 3 列大于 1000 则显示第 3 列的值,否则显示 “system id”,并且将冒号 “:” 视为分割符

# awk -F: 'BEGIN{max=1000}{id=($3>max ?$3 :"system id");print id}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.5 如果第 1 列的字符串的长度大于 3 则显示第 1 列的内容

# awk '{if (length($1) > 3) print $1}' test.txt

13.6 当第 2 列等于大写的 “A” 或者小写的 “a” 时则显示此行的第 2 列

# awk '{IGNORECASE=1;if($2=="a"){print $2}}' test.txt

内容十四:awk for 循环统计的案例

14.1 创建数组 a,其中 a[0]=0 a[1]=11 a[2]=22,并且将数组 a 显示出来

# awk 'BEGIN{a[0]=0;a[1]=11;a[2]=22;for(i in a){print i,a[i]}}'

14.2 统计所有的 IP 地址以及每 1 个 IP 地址访问的次数

# awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log


补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例

14.3 统计所有的 IP 地址以及每 1 个 IP 地址访问的次数,并以从多到少的顺序进行排序

# awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log | sort -k2nr


补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例

14.4 统计所有的 IP 地址以及每 1 个 IP 地址非 200 状态访问的次数

# awk '$8!=200{ip[$1]++}END{for(i in ip){print ip[i],i}}' /var/log/httpd/access_log

(
补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例
)

14.5 统计所有的 IP 地址以及每 1 个 IP 地址非 200 状态访问的次数,并只显示数量最多的前 10 个 IP 地址和其对应的次数

# awk '$8!=200{ip[$1]++}END{for(i in ip){print ip[i],i}}' /var/log/httpd/access_log | sort -k1nr | head -n

(
补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例
)

或者:

# awk '$8!=200{ip[$1]++}END{PROCINFO["sorted_in"]="@val_num_desc";for(i in ip){if(count++=10){exit}print ip[i],i}}' /var/log/httpd/access_log

(
补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例
3) 这里的 PROCINFO[“sorted_in”]=”@val_num_desc” 表示在 for(i in ip) 时通过 ip[i] 的数值进行降序排序,也就是 ip 数组中,最大的一个值会排在前面

14.6 统计 TCP 连接出现的次数

# ss -ntulap 2> /dev/null | awk '/^tcp/{tcp[$6]++} END{for(i in tcp){print i,tcp[i]}}'

或者:

# ss -ntulap 2> | grep 'tcp' | awk '{print $6}' | sort | uniq -c

14.7 统计每个网页被各个 IP 地址访问的次数,并以每个网页和 IP 地址作为文件名创建文件,里面存储着此网页被访问的 IP 地址和对应的访问次数

# vim awk.txt

创建以下内容:

BEGIN{
    FS=" "
}
{
    if(!arr[$1,$2]++){
        arr1[$1]++
    }
}

END{
    for(i in arr){
        print i,arr[i] >(i".txt")
    }
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 这里以统计 test.txt 文件里第 1 列的网页和第 2 列第 IP 地址为例
4) 这里当把 arr[$1,$2] 放到 if 的小括号 “()” 里面时,它是 1 个判断,当 arr[$1,$2] 第 1 次遭遇某个内容时,arr[$1,$2] 此时的值是 0 ,然后才进行 ++ (也就是加 1),而当 arr[$1,$2] 的值是 0 时,给他取反的 !arr[$1,$2] 就是布尔真,而布尔为真时就执行 arr1[$1]++
5) 当后来 arr[$1,$2] 不是第 1 次遭遇某个内容时,arr[$1,$2] 的值就不是 0 了,而给他取反的 !arr[$1,$2] 就是布尔假了,也就不会再执行后面的 arr1[$1]++

或者:

# vim awk.txt

创建以下内容:

BEGIN{
    FS=" "
}

!arr[$1,$2]++ {
    arr1[$1]++
}

END{
    for(i in arr){
        print i,arr[i] >(i".txt")
    }
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 这里以统计 test.txt 文件里第 1 列的网页和第 2 列第 IP 地址为例
4) 这里当把 arr[$1,$2] 放到大括号 “{}” 外面时它自身就是 1 个判断,当 arr[$1,$2] 第 1 次遭遇某个内容时,arr[$1,$2] 此时的值是 0 ,然后才进行 ++ (也就是加 1),而当 arr[$1,$2] 的值是 0 时,给他取反的 !arr[$1,$2] 就是布尔真,而布尔为真时就执行 arr1[$1]++
5) 当后来 arr[$1,$2] 不是第 1 次遭遇某个内容时,arr[$1,$2] 的值就不是 0 了,而给他取反的 !arr[$1,$2] 就是布尔假了,也就不会再执行后面的 arr1[$1]++

14.8 将每 1 列的值前面加上所属行的行号,按行单独显示出来

# awk -F: '{i=1;while(i<NF) {print NR,$i;i++}}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

或者:

# awk -F: '{for(i=1;i<NF;i++)  {print NR,$i}}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

14.9 显示九九乘法表

# seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'


补充:
1) 执行 seq 9 命令,第 1 行输出 1,第 2 行输出 2,以此类推直到第 9 行,并将结果传输给 sed 命令
2) 执行 sed 的 H 参数,将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 g 参数。当执行到第 1 行时,添加到 hold space (保持空间) 是 1,hold space (保持空间) 里的值是 \n1,当时执行到第 2 行时,添加到 hold space (保持空间) 是 2,hold space (保持空间) 里的值是 \n1\n2,依次类推
3) 执行 sed 的 g 参数,将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除。当执行到第 1 行时,hold space (保持空间)里的值是 \n1,pattern space (模型空间) 里的值将是 \n1,显示的结果也会是 1,当执行到第 2 行时,hold space (保持空间)里的值是 \n1\n2,pattern space (模型空间) 里的值将是 \n1\n2,显示的结果也会是 \n1\n2,依次类推,并将结果传输给命令 awk
3) awk,将多行视为 1 行,之后执行从 1 到此行列数次循环,变量 i 初始时的值为 1,每执行 1 次则变量 i 到值会加 1,每次循环时会显示:“此时变量 i 的值”*“此行的列数”=“此时变量 i 的值和此行的列数的乘积““当此时变量 i 的值等于此行的列数时则换行,否则的话则显示制表符的空格长度”。当执行到第 1 行时显示的是 1×1=1,当执行到第 2 行时显示的是 1×2=2 2×2=4,以此类推

内容十五:awk 表格显示的案例

在开始显示自定义的 1 行,在结尾再显示自定义的 1 行,并且将 “:” 视为分割符

# awk -F: 'BEGIN{print "User\tUID\tHome"}{print $1 "\t"  $3  "\t"  $6}END{print "Total",NR,"lines."}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

内容十六:awk 精确时间处理的案例

16.1 将当前系统转换成 epoch 值

# awk 'BEGIN{print systime()}'

16.2 将 2019 年 01 月 01 日 01 时 01 分 01 秒转换成 epoch 值

# awk 'BEGIN{print mktime("2019 01 01 01 01 01")}'

或者:

# awk 'BEGIN{print mktime("2019 1 1 1 1 1")}'

或者:

# vim awk.txt

创建以下内容:

BEGIN{
    str="2019-01-01T01:01:01+8:00"
    patsplit(str,arr,"[0-9]{1,4}")
    Y=arr[1]
    M=arr[2]
    D=arr[3]
    H=arr[4]
    m=arr[5]
    S=arr[6]
    print mktime(sprintf("%s %s %s %s %s",Y,M,D,H,m,S))}
}
# awk -f awk.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) patsplit 函数的格式是:patsplit(<string>,<array>,”<regular expression>”),其中第 1 个是要匹配的字符串,第 2 个是要匹配结果存储进去的数组,第 3 个是正则表达式,通过正则表达式匹配字符串,然后将匹配成功的部分保存到数组中去
3) mktime 函数的格式是:mktime(‘YYYY MM DD HH mm SS [D9T]’),作用是:构建时间,如果构建成功则显示这个时间点秒级的 epoch 值,如果构建失败则返回 -1

16.3 将 2019 年 01 月 01 日 01 时 01 分 01 秒的时间点转换成 epoch 值

# date -d '@1546275661' +"%F %T"

16.4 显示从 2021-01-01T01:01:01+08:00 开始到结尾的所有日志,以时间被中括号扩起来作为时间到格式,例如:[2021-01-01T01:01:01+8:00] (处理只包含数字的时间)

# vim awk.txt

创建以下内容:

BEGIN{
  str="2021-01-01T01:01:01+08:00"
  choose_time = mktime("2021 01 01 01 01 01")
}

function strptime_now(str   ,arr,Y,M,D,H,m,S) {
  patsplit(str,arr,"[0-9]{1,4}")
  Y=arr[1]
  M=arr[2]
  D=arr[3]
  H=arr[4]
  m=arr[5]
  S=arr[6]
  return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}

{
  #[2022-01-01T01:01:01+8:00]
  match($0,"^.*\\[(.*)\\].*",arr)
  tmp_time = strptime_now(arr[1])
  if(tmp_time > choose_time){
    print
  }
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 默认情况下 Y=arr[1] 等是在定义全局变量,这里的 strptime(str ,arr,Y,M,D,H,m,S) 是指将全局变量转换成局部变量,这样此函数执行完以后变量就被删除了
4) patsplit 函数的格式是:patsplit(<string>,<array>,”<regular expression>”),其中第 1 个是要匹配的字符串,第 2 个是要匹配结果存储进去的数组,第 3 个是正则表达式,通过正则表达式匹配字符串,然后将匹配成功的部分保存到数组中去
5) mktime 函数的格式是:mktime(‘YYYY MM DD HH mm SS [D9T]’),作用是:构建时间,如果构建成功则显示这个时间点秒级的 epoch 值,如果构建失败则返回 -1
6) sprintf 函数的格式是:sprintf(“s%……”,<variable>……),作用是:显示变量里的内容,其中第 1 个是占位符,第 2 个是变量
7) match 函数的格式是:match(<column>,”<regular expression>”,<array>),其中第 1 个是要匹配的列,第 2 个是正则表达式,第 3 个是要匹配结果存储进去的数组,作用是:以正则表达式匹配匹配某列,并将匹配结果存储在数组里

16.5 显示从 01/Jan/2022:21:30:30+8:00 开始到结尾的所有日志,以时间被中括号扩起来作为时间到格式,例如:[01/Jan/2022:21:30:30+8:00] (处理只包含英文月份的时间)

# vim awk.txt

创建以下内容:

BEGIN{
  str="01/Jan/2022:21:30:30+8:00"
  choose_time = mktime("2020 01 01 01 01 01")
}

{
  #[01/Jan/2022:21:30:30+8:00]
  match($0,"^.*\\[(.*)\\].*",arr)
  tmp_time = strptime(arr[1])
  if(tmp_time > choose_time){
    print
  }
}

# 01/Jan/2022:21:30:30+8:00
function strptime(str   ,dt_str,arr,Y,M,D,H,m,S) {
  dt_str = gensub("[/:+-]"," ","g",str)
  # 01 Jan 2020 01 01 01 01 01
  split(dt_str,arr," ")
  Y=arr[3]
  M=mon_map(arr[2])
  D=arr[1]
  H=arr[4]
  m=arr[5]
  S=arr[6]
  return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}

function mon_map(str   ,mons){
  mons["Jan"]=1
  mons["Feb"]=2
  mons["Mar"]=3
  mons["Apr"]=4
  mons["May"]=5
  mons["Jun"]=6
  mons["Jul"]=7
  mons["Aug"]=8
  mons["Sep"]=9
  mons["Oct"]=10
  mons["Nov"]=11
  mons["Dec"]=12
  return mons[str]
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 默认情况下 Y=arr[1] 等是在定义全局变量,这里的 strptime(str ,arr,Y,M,D,H,m,S) 是指将全局变量转换成局部变量,这样此函数执行完以后变量就被删除了
4) gensub 函数的格式是:gensub(“<original character>”,”<new character>”,”g”,<string>),作用是:将字符串中的某些字符进行替换,其中第 1 个是原字符,第 2 个是新字符,第 3 个是要操作的字符串
5) split 函数的格式是:split(<string>,<array>,”<separator>”),作用是:将字符串里的内容分开存储到数组里,其中第 1 个是要操作的字符串,第 2 个是要存入内容的数组,第 3 个是分隔符
6) mktime 函数的格式是:mktime(‘YYYY MM DD HH mm SS [D9T]’),作用是:构建时间,如果构建成功则显示这个时间点秒级的 epoch 值,如果构建失败则返回 -1
7) sprintf 函数的格式是:sprintf(“s%……”,<variable>……),作用是:显示变量里的内容,其中第 1 个是占位符,第 2 个是变量
8) match 函数的格式是:match(<column>,”<regular expression>”,<array>),其中第 1 个是要匹配的列,第 2 个是正则表达式,第 3 个是要匹配结果存储进去的数组,作用是:以正则表达式匹配匹配某列,并将匹配结果存储在数组里

(注意:在 awk 中反译符号 “\“ 前面还必须要要有一个反译符号 ”\“,例如,如果要显示一个前引号 “””: \”)

内容十七:awk 同时处理 2 个文件的案例

先把第 2 个文件的第 5 列删除,再用第 2 个文件的第 1 列减去第 1 个文件的第 1 列,再把得到的结果粘贴到原来第 2 个文件第 5 列对应的位置

# vim awk.txt

创建以下内容:

{
  f1 = $1
  if( (getline < "test2.txt") >= 0 ){
    $5 = $1 - f1
    print $0
  }
}
# awk -f awk.txt test1.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test1.txt 和 test2.txt 是要被 awk 操作的测试文件
4) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第 1 行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
5) 执行 getline < “test2.txt” 1 行 1 行地读取 test2.txt 文件里的内容

(注意:这里的 print $0 会根据 OFS 重建 $0,而默认情况下 OFS 是 1 个空格,所以这里重建后,列和列之间也会是 1 个空格)

或者:

# awk 'NR==FNR{arr[FNR]=$1}NR!=FNR{$5 = $1 - arr[FNR];print $0}' test1.txt test2.txt

(补充:这里 NR == FNR 的时候表示处理的是第 1 个文件,只有当处理第 1 个文件中时 NR 等于 FNR)