语法格式:
格式1:command | awk [选项] ‘BEGIN{指令}[条件]{指令}END{指令}’
格式2:awk [选项] ‘BEGIN{指令}[条件]{指令}END{指令}’ 文件
内置变量
表达式 | 含义 |
---|---|
NR | 行号 |
NF | 每行列的总数 |
FS | 字段分隔符,用法:-vFS=separator |
OFS | 输出字段分隔符, 用法:-vOFS=separator |
FNR | 记录当前行的行号 |
RS:读取分隔符,默认是按行读取 |
RS=””:按空行为分隔符读取 |
RS=”reg”:按照reg为分隔符读取 | |
RS=”^$”:一次性读取所有的数据 | |
RS=”\n+”:按行读取,但忽略所有的空行 | |
-F: 指定字段分隔符,默认的空格,tab | |
$0 | 整行 |
$1 | 第一列 |
$2 | 第二列 |
$3 | 第三列 |
… | … |
$NF | 最后一列 |
运算符
算数运算符
+:浮点数运算
➜ Desktop awk 'BEGIN{a=2.5;b=3.25;print a+b}'
5.75
+=:统计文本的字段数
➜ Desktop awk -F':' 'BEGIN{sum=0}{sum+=NF}END{print sum}' /etc/passwd
119
++:统计以bash结尾的个数
➜ Desktop awk 'BEGIN{sum=0}/\<bash$/{sum++}END{print sum}' /etc/passwd
1
逻辑运算符
&&:期望多个条件都成立
# 列出第三个字列的值是1-8的行
➜ Desktop awk -F':' '$3 >= 1 && $3 <= 8 {print}' /etc/passwd
bin:x:1:1::/:/usr/bin/nologin
daemon:x:2:2::/:/usr/bin/nologin
mail:x:8:12::/var/spool/mail:/usr/bin/nologin
||:只要有一个条件满足就输出
# 输出第三列的值是1或者8的行
➜ Desktop awk -F':' '$3 == 1 || $3 == 8 {print}' /etc/passwd
bin:x:1:1::/:/usr/bin/nologin
mail:x:8:12::/var/spool/mail:/usr/bin/nologin
正则
/content/:过滤含有content的行
# 过滤含有root的行
➜ Desktop awk '/root/{print}' /etc/passwd
root:x:0:0::/root:/bin/bash
/content1/,/content2/:读取一段文本,从content1开始过滤,过滤到content2
# 从root过滤到daemon
➜ Desktop awk '/root/,/daemon/{print}' /etc/passwd
root:x:0:0::/root:/bin/bash
bin:x:1:1::/:/usr/bin/nologin
daemon:x:2:2::/:/usr/bin/nologin
打印某个字段以bash结尾的行
# 打印第7个字段是以bash结尾的行
➜ Desktop awk -F':' '$7~/bash$/{print $7}' /etc/passwd
/bin/bash
打印某个字段不以bash结尾的行
# 打印第7个字段是不以bash结尾的行
➜ Desktop awk -F':' '$7!~/bash$/{print $7}' /etc/passwd
/bin/bash
流程控制
单分支:if(){}
# 统计第三列的数值大于等于500的个数
➜ Desktop awk -F':' 'BEGIN{sum=0}{if($3 >= 500) {sum++}}END{print sum}' /etc/passwd
9
双分支:if(){}else{}
# 统计大于等于500的个数, 小于500的个数
➜ Desktop awk -F':' 'BEGIN{i;j}{if($3 >= 500) {i++} else {j++}}END{print "大于等于500的个数:"i",小于500的个数:"j}' /etc/passwd
大于等于500的个数:9,小于500的个数:8
多分支:if(){}else if{}else{}
# 统计第三列小于500的个数,500-1000的个数据,大于1000的个数
➜ Desktop awk -F':' 'BEGIN{a;b;c}{if($3 <= 500) {a++} else if($3 <= 1000){b++} else {c++}}END{print "小于等于500的个数:"a",500-1000之间的个数:"b",大于1000的个数:"c}' /etc/passwd
小于等于500的个数:8,500-1000之间的个数:8,大于1000的个数:1
循环结构
while(条件){循环体}
# 统计root单词的个数
➜ Desktop awk -F ":|/" 'BEGIN{sum}{while(i<=NF){if($i==root){sum++};i++}}END{print sum}' /etc/passwd
5
for(初值;条件;步长){循环体}
# 打印10以内的基数
➜ Desktop awk 'BEGIN{for(i=0;i<=10;i++){if(i%2==1){print i}}}'
1
3
5
7
9
for(key in arr){循环体}
# 统计每个单词的个数
➜ Desktop awk -F':|/' '{for(i=1;i<=NF;i++){if($i~/^$/){continue};arr[$i]++}}END{for(key in arr){print key,arr[key]}}' /etc/passwd | sort -rn -k2 | head
bin 18
x 17
usr 15
nologin 14
sunzhengbo 2
srv 2
root 2
mail 2
http 2
ftp 2
➜ Desktop awk -F':|/' '{for(i=1;i<=NF;i++){if($i~/^$/){continue};arr[$i]++}}END{PROCINFO["sorted_in"]="@val_num_desc";for(key in arr){if(counter++==10){exit}print key,arr[key]}}' /etc/passwd
bin 18
x 17
usr 15
nologin 14
sunzhengbo 2
srv 2
root 2
mail 2
http 2
ftp 2
流程控制关键字
关键字 | 含义 |
---|---|
break | 结束当前的循环体 |
continue | 结束当次循环,进入下次循环 |
next | 跳过当前行,读取下一行,如:awk ‘NF<2{next}{print}’ xxx.log |
exit | 结束文本读取,转入END{} |
数组
进阶
去重
➜ Desktop awk -F'/' '{arr[$NF]++;if(arr[$NF]==1){print}}' /etc/passwd
root:x:0:0::/root:/bin/bash
bin:x:1:1::/:/usr/bin/nologin
git:x:975:975:git daemon user:/:/usr/bin/git-shell
sunzhengbo:x:1000:1000::/home/sunzhengbo:/bin/zsh
# 可以简写为:
# arr[key]不存在的情况下执行++是false,再取反就是true,
# 后面再执行就变成true,取反是false,所以起到去重的作用
➜ Desktop awk -F'/' '!arr[$NF]++' /etc/passwd
root:x:0:0::/root:/bin/bash
bin:x:1:1::/:/usr/bin/nologin
git:x:975:975:git daemon user:/:/usr/bin/git-shell
sunzhengbo:x:1000:1000::/home/sunzhengbo:/bin/zsh
指定分隔符
➜ Desktop awk 'BEGIN{FS="/"}{arr[$NF]++;if(arr[$NF]==1){print}}' /etc/passwd
root:x:0:0::/root:/bin/bash
bin:x:1:1::/:/usr/bin/nologin
git:x:975:975:git daemon user:/:/usr/bin/git-shell
sunzhengbo:x:1000:1000::/home/sunzhengbo:/bin/zsh
插入字符
➜ Desktop echo 'a b e' | awk '{$2=$2" c d";print}'
a b c d e
# 修改字段后会重建$0的OFS
➜ Desktop echo 'a b e' | awk '{$2=$2" c d";print}'
a b c d e
格式化输出
- 设置输出分隔符
- 必须修改字段,因为修改字段才会使OFS生效
➜ Desktop echo 'a b c d e\naa bb cc dd ee\naaa bbb cc d e' | awk 'BEGIN{OFS="\t"}{$1=$1;print}' a b c d e aa bb cc dd ee aaa bbb cc d e
按段落读取
mdatacenter_every_month_data.txt➜ Desktop awk 'BEGIN{RS="project";sum=0}NR>1{result=$8*$NF;print $2$8" * "$NF" = "result"M";sum+=result}END{print "total = "sum/1024"G"}' mdatacenter_every_month_data.txt 50 * 92.141M = 4607.05M 23 * 226.252M = 5203.8M 24 * 315.571M = 7573.7M 81 * 158.543M = 12842M 2 * 11.481M = 22.962M 39 * 211.513M = 8249.01M 42 * 48.530M = 2038.26M 20 * 150.899M = 3017.98M 20 * 189.790M = 3795.8M 32 * 147.368M = 4715.78M 20 * 167.853M = 3357.06M 19 * 164.603M = 3127.46M 48 * 182.207M = 8745.94M 16 * 159.386M = 2550.18M 21 * 144.483M = 3034.14M 15 * 138.930M = 2083.95M 15 * 191.698M = 2875.47M 95 * 142.026M = 13492.5M 238 * 170.142M = 40493.8M 19 * 152.516M = 2897.8M 2 * 2372.505M = 4745.01M total = 136.201G
替换:
gsub:全局替换
gsub(r,s) 在整个$0中用s替代r
gsub(r,s,t) 在整个t中用s替代rsub:仅替换第一次匹配的内容
sub (regular expression, substitution string)
sub (regular expression, substitution string, target string)➜ Desktop awk 'BEGIN{RS="project";sum=0}NR>1{result=$8*$NF;gsub(",",": ", $2);print $2 $8" * "$NF" = "result"M";sum+=result}END{print "total = "sum/1024"G"}' mdatacenter_every_month_data.txt 郝家营: 50 * 92.141M = 4607.05M 太和仙: 23 * 226.252M = 5203.8M 武川: 24 * 315.571M = 7573.7M 呱呱山: 81 * 158.543M = 12842M 雅培: 2 * 11.481M = 22.962M 睢宁魏集核源: 39 * 211.513M = 8249.01M 广西灵山: 42 * 48.530M = 2038.26M 仰天湖二期: 20 * 150.899M = 3017.98M 五寨: 20 * 189.790M = 3795.8M 北山梁: 32 * 147.368M = 4715.78M 虞城: 20 * 167.853M = 3357.06M 丰顶山: 19 * 164.603M = 3127.46M 尖尖山: 48 * 182.207M = 8745.94M 龙潭: 16 * 159.386M = 2550.18M 新野: 21 * 144.483M = 3034.14M 火木梁: 15 * 138.930M = 2083.95M 龙门: 15 * 191.698M = 2875.47M 太仆寺旗: 95 * 142.026M = 13492.5M 乌达来: 238 * 170.142M = 40493.8M 上蔡: 19 * 152.516M = 2897.8M 成都领克: 2 * 2372.505M = 4745.01M total = 136.201G
awk 换行书写
```bash ➜ Desktop awk ‘ BEGIN{ RS=”project” sum=0 }
NR>1{ result=$8$NF sub(“,”, “:\t”, $2) print $2 $8” “$NF” = “result”M” sum+=result }
END{ print “total: “sum/1024”G” } ‘ mdatacenter_every_month_data.txt 郝家营: 50 92.141M = 4607.05M 太和仙: 23 226.252M = 5203.8M 武川: 24 315.571M = 7573.7M 呱呱山: 81 158.543M = 12842M 雅培: 2 11.481M = 22.962M 睢宁魏集核源: 39 211.513M = 8249.01M 广西灵山: 42 48.530M = 2038.26M 仰天湖二期: 20 150.899M = 3017.98M 五寨: 20 189.790M = 3795.8M 北山梁: 32 147.368M = 4715.78M 虞城: 20 167.853M = 3357.06M 丰顶山: 19 164.603M = 3127.46M 尖尖山: 48 182.207M = 8745.94M 龙潭: 16 159.386M = 2550.18M 新野: 21 144.483M = 3034.14M 火木梁: 15 138.930M = 2083.95M 龙门: 15 191.698M = 2875.47M 太仆寺旗: 95 142.026M = 13492.5M 乌达来: 238 170.142M = 40493.8M 上蔡: 19 152.516M = 2897.8M 成都领克: 2 * 2372.505M = 4745.01M total: 136.201G
<a name="QARzd"></a>
### 调用系统命令
awk也是一种语言,所以不能直接在awk中直接使用系统命令,需要使用system()才能执行系统命令
<a name="O64Gq"></a>
#### 批量修改文件名
```bash
$ find $PWD -name "m*" | awk -vFS=/ -vOFS=/ '{raw=$0;sub("m", "M", $NF);c="mv "raw" "$0; print c; system(c)}'
mv /d/Backup/bjwater/machines.bson /d/Backup/bjwater/Machines.bson
mv /d/Backup/bjwater/machines.metadata.json /d/Backup/bjwater/Machines.metadata.json
mv /d/Backup/bjwater/measurements.bson /d/Backup/bjwater/Measurements.bson
mv /d/Backup/bjwater/measurements.metadata.json /d/Backup/bjwater/Measurements.metadata.json