[命令] 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

内容七: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 表格显示的案例

在开始显示一行,在结尾显示一行,并且将 “:” 视为分割符

# 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)

[内容] Linux RPM 软件包的解压 (判断安装一个 RPM 文件之后哪些目录和文件会被创建)

内容一:RPM 简介

1) RPM 的全名是 RPM Package Manager
1) RPM 是 CentOS、RHEL、openSUSE、SUSE 的软件安装包
2) RPM 是使用 cpio 格式压缩成的包

内容二:解压 RPM 文件的方法

#  rpm2cpio <文件名> | cpio -div

(补充:解压之后就可以看到如果安装这个 RPM 文件的话,有哪些目录和文件会被创建)

[实验] Nginx 源码软件包的安装

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

软件准备:

在 Nginx 官网上下载搭建集群所需软件 Nginx:

http://nginx.org/en/download.html

正文:

步骤一:系统环境要求

(1)服务器的系统需要是 CentOS Linux 7 版本
(2)服务器系统需要有 yum 源

步骤二:安装 Nginx 的依赖软件

# yum -y install gcc pcre-devel openssl-devel

步骤三:安装 Nginx

3.1 添加一个用于启动 Nginx 的用户身份

# useradd -s /sbin/nologin nginx

3.2 解压 Nginx 安装包

# tar -xvf nginx-1.16.1.tar.gz

(补充:这里要安装的 Nginx 版本是 1.16.1)

3.3 进入 Nginx 安装包目录

# cd nginx-1.16.1

(补充:这里要安装的 Nginx 版本是 1.16.1)

3.4 配置 Nginx

# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module

3.5 编译并安装 Nginx

# make && make install

步骤四:测试 Nginx

4.1 启动 Nginx

# /usr/local/nginx/sbin/nginx

4.2 访问 Nginx 实现的网页服务

# curl 127.0.0.1

4.3 显示已安装 Nginx 的版本

# /usr/local/nginx/sbin/nginx -V

[实验] Linux RPM 软件包的制作 (通过 rpm-build 实现) (Nginx 版)

注意:

文中的很多信息例如软件的名称等是站主在本次操作中随意取的名称,读者可以根据自己的喜好换成任意别的名称

软件准备:

在 Nginx 官网上下载搭建集群所需软件 Nginx:

http://nginx.org/en/download.html

正文:

步骤一:系统环境要求

1) 服务器的系统是 CentOS 7 版本
2) 服务器系统要配置好 YUM 源

步骤二:准备 rpm-build 软件

2.1 安装 rpm-build

# yum -y install rpm-build

2.2 生成 rpm-build 目录

# rpmbuild -ba nginx.spec
error: failed to stat /root/nginx.spec: No such file or directory

2.3 显示生成的 rpm-build 目录

# ls /root/rpmbuild/
BUILD  BUILDROOT  RPMS	SOURCES  SPECS	SRPMS

步骤三:制作 Nginx 软件的 RPM 包

3.1 将 Nginx 的源码安装包放在固定的位置

# cp nginx-1.16.1.tar.gz /root/rpmbuild/SOURCES/

(补充:这里要安装的 Nginx 版本是 1.16.1)

3.2 制作 Nginx 的 nginx.spec 文件

# vi /root/rpmbuild/SPECS/nginx.spec

将部分内容修改如下:

Name:nginx
Version:1.16.1
Release:1.0
Summary:Nginx is a web server software.
......
License:GPL
URL:www.nginx.org
Source0:%{name}-%{version}.tar
......
%description
nginx is an HTTP and reverse proxy server ......
......
%post
useradd nginx
......
%prep
%setup -q
......
%build
./configure --user=nginx --group=nginx --with-http_ssl_module
make %{?_smp_mflags}
%install
make install DESTDIR=%{buildroot}
......
%files
......
%doc
/usr/local/nginx/*
......
%changelog


补充:
1) 这里的 Name:nginx 是指软件名称是 nginx
2) 这里的 Version:1.16.1 是指软件的版本是 1.16.1
3) 这里的 Release:1.0 是指发布的版本是 1。0
4) 这里的 Summary: Nginx is a web server software. 是指软件的描述是 Summary: Nginx is a web server software.
5) 这里的 License:GPL 是指软件使用的协议是 GPL
6) 这里的 URL:www.nginx.org 是指软件的官网是 www.nginx.org
7) 这里的 Source0:%{name}-%{version}.tar 是指软件源码文件的名称
8) 这里的 #BuildRequires: 是指软件编译安装时需要的依赖包,这里没有内容
9) 这里的 #Requires: 是指软件安装时所需要的依赖包,这里没有内容
10) 这里的 %description 是指软件的详细描述,这里没有内容
11) 这里的

%post
useradd nginx

是指软件安装后创建 nginx 用户

12) 这里的 %prep 是指软件安装前的准备,这里没有内容
13) 这里的 %setup –q 是指自动解压软件的源码包,并 cd 进入刚刚解压出来的目录
14) 这里的

%build
./configure
make %{?_smp_mflags}

是指对源码安装包进行配置
15) 这里的

%install
make install DESTDIR=%{buildroot}

是指对源码安装包进行编译安装
16) 这里的

%files
%doc
/usr/local/nginx/*

是指将源码安装包安装到 /usr/local/nginx/ 目录里

3.4 安装 Nginx 软件的依赖包

# yum -y install gcc pcre-devel openssl-devel

3.5 生成 Nginx rpm 软件包

# rpmbuild -ba /root/rpmbuild/SPECS/nginx.spec

3.6 显示新生成的 Nginx rpm 软件包

# rpm -qpi /root/rpmbuild/RPMS/x86_64/nginx-1.16.1-1.0.x86_64.rpm

步骤四:使用新生成的 Nginx rpm 软件包

4.1 安装刚刚新生成的 Nginx rpm 软件包

# rpm -ivh /root/rpmbuild/RPMS/x86_64/nginx-1.16.1-1.0.x86_64.rpm

4.2 启动 Nginx 服务

# /usr/local/nginx/sbin/nginx

4.3 显示 Nginx 服务的启动状态

4.3.1 显示 Nginx 网页是否可以访问
# curl http://127.0.0.1/
4.3.2 显示 Nginx 的端口有没有启动
# ss -ntulap | grep 80

[命令] Linux 命令 chattr (文件锁)

内容一: chattr 格式

1.1 锁定文件的格式(文件将无法被修改、追加和删除)

# chattr +i <file>

1.2 解锁文件的格式(文件将可以被修改、追加和删除)

# chattr -i <file>

1.3 锁定文件后仅可追加内容的格式(文件将无法被修改和删除,但是可以被追加新内容)

# chattr +a <文file> 

1.4 解锁文件只可被追加内容的格式(文件将取消无法被修改和删除,但红四可以被追加新内容的状态)

# chattr -a <file> 

1.5 显示文件特殊属性

# lsattr <file> 

内容二: chattr 的使用案例

2.1 案例一:锁定文件并显示文件的锁定状态

2.1.1 锁定文件
# chattr +i /etc/resolv.conf

(补充:这里以锁定 /etc/resolv.conf 文件为例)

2.1.2 显示文件的锁定状态
# lsattr /etc/resolv.conf
----i----------- /etc/resolv.conf

(补充:这里以显示 /etc/resolv.conf 文件为例)

2.1.3 解锁文件
# chattr -i /etc/resolv.conf

(补充:这里以解锁 /etc/resolv.conf 文件为例)

2.2 案例二:锁定文件后仅可追加内容并显示文件的锁定状态

2.2.1 锁定文件后仅可追加内容
# chattr +a /etc/hosts

(补充:这里以锁定 /etc/hosts 文件为例)

2.2.2 显示文件的锁定状态
# lsattr /etc/hosts
----a---------- /etc/hosts

(补充:这里以显示 /etc/hosts 文件为例)

2.2.3 解锁仅可追加内容的文件
# chattr -a /etc/hosts

(补充:这里以解锁 /etc/hosts 文件为例)