1. 基本知识
1.1. 名称由来
awk是其设计者名字首字母.
1.2. 语法格式
awk [options] 'pattern {action}' filename
1.3. 执行流程
对如下语句:
awk 'BEGIN {action1} pattern {action2} END {action3}' filename
执行流程如下:
- 执行BEGIN {action1}语种块中的语句.
- 从文件或STDIN逐行读取, 对每一行执行pattern {action2}, 直到全部读取完成.
- 执行END {action3}语种块中的语句.
例子: 打印每一行, 并且在开头和结尾分别打印Start和END.print语句不带参数时就打印整行内容
例子:对第2列进行累加, 在BEGIN中为i赋初值, 在END中打印i(累加结果).
$ echo -e "a 1 a
a 2 a
a 3 a" |
> awk 'BEGIN {i=0} {i+=$2} END {print i}'
6
1.4. 术语解释
术语 | 说明 |
---|---|
记录 | awk把每一个以换行符结束的行称为一个记录, NR即记录的个数 |
域 | 记录中每个单词称做"域", 默认以空格或Tab分隔, NF即域的个数. |
2. options
选项 | 说明 |
---|---|
-v | 参数传递 |
-f | 指定脚本文件 |
-F | 指定分隔符 |
-V | 查看awk版本号 |
例子1:指定列分隔符
文件内容
$ cat demo.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
通过-F指定列分隔符
$ # -F和:之间没有空格
$ awk -F: '{printf "%-6s %-9s %s
", $1, $6, $7}' demo.txt
root /root /bin/bash
daemon /usr/sbin /usr/sbin/nologin
bin /bin /usr/sbin/nologin
sys /dev /usr/sbin/nologin
sync /bin /bin/sync
$
$ # -F和:之间有空格, 这时分隔符最好放到引号中, 否则使用#等分隔时会报错.
$ awk -F ':' '{printf "%-6s %-9s %s
", $1, $6, $7}' demo.txt
root /root /bin/bash
daemon /usr/sbin /usr/sbin/nologin
bin /bin /usr/sbin/nologin
sys /dev /usr/sbin/nologin
sync /bin /bin/sync
通过-v FS指定列分隔符
$ awk -v FS=: '{printf "%-6s %-9s %s
", $1, $6, $7}' demo.txt
root /root /bin/bash
daemon /usr/sbin /usr/sbin/nologin
bin /bin /usr/sbin/nologin
sys /dev /usr/sbin/nologin
sync /bin /bin/sync
3. pattern {action}
awk_script由pattern和Action组成,
例如:
awk 'BEGIN {action1} pattern {action2} END {action3}' filename
模式:有4种模式
- /正则表达式/
- 关系表达式:使用运算符进行操作, 比如字符串、数字的比较测试.
- 模式匹配表达式:用运算符:~和~!, 分别表示匹配、不匹配.
- BEGIN/END语句块、pattern语句块.
action:由一个或多个命令/函数/表达式组成, 它们之间用换行符或分号隔开.
action主要有以下类型:
- 变量或数组赋值
- 输出命令
- 内置函数
- 控制流语句
注意:
- BEGIN {action1}, 只在开始执行一次, 是可选的, 默认无BEGINaction.
- pattern {action2}, 对匹配pattern的行进行操作, pattern是可选的, 默认对每一行进行action2, {action2}也是可选的, 默认打印匹配pattern的行, 但是pattern和{action2}必须出现一个, 不能都省略.
- END {action3}, 只在结尾执行一次, 是可选的, 默认无END action.
例子1:
输入文件内容:
$ cat a.txt
line first 1
line second 2
line third 3
line forth 4
line fifth 5
line sixth 6
line seventh 7
使用BEGIN、END等
$ # 在BEGIN的action中, 先打印一行内容, 再给i赋初值
$ # 在遍历line的语句中, 打印第2、3列, 并对第3列累加
$ # 在END的action中, 打印第3列的累加结果
$ awk 'BEGIN {print "C2", "C3"; i=0} {print $2, $3; i+=$3} END {print "total=",i}' a.txt
C2 C3
first 1
second 2
third 3
forth 4
fifth 5
sixth 6
seventh 7
total= 28
注意:
- 在print语句中,
字符串要写在双引号里,
字符串之间用逗号分隔,
变量可以直接赋初值,
变量使用时不能带双引号 - 变量赋值和使用时不需要带$号($1/$2等不受此限制)
- 多个action语句之间使用分号分隔.
4. 运算符
运算符 | 说明 |
---|---|
赋值运算符 | -------- |
= | 赋值语句 |
逻辑运算符 | -------- |
|| | 逻辑或 |
&& | 逻辑与 |
正则运算符 | -------- |
~ | 正则匹配 |
~! | 正则不匹配 |
关系运算符 | -------- |
<, <= | 小于, 小于等于 |
>, >= | 大于, 大于等于 |
== | 等于 |
!= | 不等于 |
算术运算符 | -------- |
+, - | 加, 减 |
*, /, % | 乘, 除, 取余 |
+, -, ! | 一元加/减/非 |
^, *** | 幂 |
++, -- | 自增, 自减 |
其它运算符 | -------- |
$ | 字段引用 |
空格 | 字符串连接 |
?: | 三元运算 |
in | 元素是否存在于数组 |
例: 只处理奇数行并输出
$ awk -F: 'NR%2==1 {printf "%s) %-6s %-9s %s
", NR, $1, $(NF-1), $NF}' demo.txt
1) root /root /bin/bash
3) bin /bin /usr/sbin/nologin
5) sync /bin /bin/sync
例: 只处理$1等于root或bin的行, 使用||
$ awk -F: '$1=="root"||$1=="bin" {printf "%s) %-6s %-9s %s
", NR, $1, $(NF-1), $NF}' demo.txt
1) root /root /bin/bash
3) bin /bin /usr/sbin/nologin
5. 内置变量
内置变量 | 说明 |
---|---|
命令行 | -------- |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
文件名 | -------- |
FILENAME | 当前文件名 |
计数 | -------- |
FNR | 各文件分别计数的行号 |
NF | 当前行的字段个数(当前行分成了几列) |
NR | 行号 |
分隔符 | -------- |
FS | 输入字段分隔符, 默认为空白字符 |
OFS | 输出字段分隔符, 默认为空白字符 |
RS | 输入记录分隔符(输入换行符) |
ORS | 输出记录分隔符(输出换行符) |
记录 | -------- |
$0 | 完整的输入记录 |
$n | 当前记录的第n个字段, 字段间由FS分隔 |
例: 使用NF, NF表示字段数, 本例中NF=7, $NF即$7表示最后一个字段, $(NF-1)表示倒数第二字段
$ awk -F: '{printf "%-6s %-9s %s
", $1, $(NF-1), $NF}' demo.txt
root /root /bin/bash
daemon /usr/sbin /usr/sbin/nologin
bin /bin /usr/sbin/nologin
sys /dev /usr/sbin/nologin
sync /bin /bin/sync
例: 使用NR, NR表示当前行的行号
$ awk -v FS=: '{printf "%s) %-6s %-9s %s
", NR, $1, $(NF-1), $NF}' demo.txt
1) root /root /bin/bash
2) daemon /usr/sbin /usr/sbin/nologin
3) bin /bin /usr/sbin/nologin
4) sys /dev /usr/sbin/nologin
5) sync /bin /bin/sync
6. 变量
awk中, 变量不需要定义就可以直接使用, 变量类型可以是数字或字符串.
例:如果第一个域匹配test, 则把第二第三个域相加并打印.
awk '$1 ~ /test/ {count=$2+$3; print count}' filename
域变量可以修改:
例: 如果第一个域等于"root", 则把它赋值为test并打印.
awk '$1=="root" {$1="test"; print}' filename
7. 条件语句
if语句
如果第三个域大于3, 则打印一个第二第三域和一个"match"字符, 各字符之间可以有空格, 也可以没有, 字符串要写到双引号中.
awk '{if ($3>3) print $2 " " $3 " " "match"}' a.txt
if-else if-else语句
有多个分支, 各分支的语句要写在大括号中.
awk '{if ($3<=2) {print "0~2"} else if ($3<=4) {print "3~4"} else {print "5~n"}}' a.txt
8. 循环语句
- awk有三种循环:while、for、special for
- 可以使用break和continue.
- next语句从输入文件中读取一行, 然后从头开始执行awk脚本.
- exit语句用于结构awk程序, 但不会略过END块.
例子:
awk '{i=1; while(i<=NF){print i, $i; i++}; print "------"}' a.txt
说明:
- NF是当前行(记录)的字段(域)数量, $i是第i个字段(从1开始).
- 注意:print语句中, i和$i是不同的东西, i是循环变量, $i是第i个字段.
- 本例以字段数为循环条件, 遍历每个字段并打印.
例子:
awk '{for (i=1;i<=NF;i++) {print i, $i}; print "------"}' a.txt
说明:作用同上, 将while语句改为for语句实现.
special for用来遍历关联数组.见关联数组.for(i in name) {print i, name[i]}
9. 数组
在awk中数据的下标可以是数字, 也可以是字母(称为关联数组, 类似于python中的字典).
例子:
awk '{name[i++]=$2} END {for (i=0;i<NR;i++) {print i, name[i]}}' a.txt
说明:
- 将每行的第二字段存入数组, 在结尾遍历数组并打印.
- name是一个数组, 不用声明, 直接赋值.
- 给数组赋值时, 直接对下标赋值, 不需要管该下标是否存在.
- 数组下标变量i自动初始化为0, 然后每处理一个记录自动加1.
- NR是当前行的行号(从1开始), 在END块中NR自动变成总行数, 用来遍历数组.
例子:
awk '/s/ {name[NR]=$2} END {for(i in name) {print i, name[i]}}' a.txt
说明:
- 如果某行匹配/s/, 则给数组赋值, 下标是行号, 值是$2, 在结尾遍历数组.
- name的下标虽然是数字, 但可以是不连续的.
10. 内置函数
https://www.gnu.org/software/gawk/manual/html_node/Built_002din.html#Built_002din
函数 | 作用 |
---|---|
toupper() | 字符串转为大写 |
tolower() | 字符串转为小写 |
length() | 返回字符串长度 |
substr() | 返回子字符串 |
sin() | 正弦 |
cos() | 余弦 |
sqrt() | 平方根 |
rand() | 随机数 |
例: 使用toupper()将第一字段转为大写
$ awk -F: '{printf "%s) %-6s %-9s %s
", NR, toupper($1), $(NF-1), $NF}' demo.txt
1) ROOT /root /bin/bash
2) DAEMON /usr/sbin /usr/sbin/nologin
3) BIN /bin /usr/sbin/nologin
4) SYS /dev /usr/sbin/nologin
5) SYNC /bin /bin/sync