awk

awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。

awk语言的最基本功能是在文件或者字符串中基于指定规则浏览和抽取信息,awk抽取信息后,才能进行其他文本操作。完整的awk脚本通常用来格式化文本文件中的信息。

1
awk '{pattern + action}' {filenames}
  • pattern [正则]表示 AWK 在数据中查找的内容
  • action 是在找到匹配内容时所执行的一系列命令
  • 花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。

模式可以是任何条件语句或复合语句或正则表达式,模式包含两个特殊字段 BEGIN 和 END,使用 BEGIN 语句设置计数和打印头,BEGIN 语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行;END 语句用来在 awk 完成文本浏览动作后打印输出文本总数和结尾状态标志,有动作必须使用{}括起来

awk执行过程分析

  1. 执行BEGIN { commands } pattern 语句块中的语句。

    ​ BEGIN语句块:在awk开始从输入输出流中读取行之前执行,在BEGIN语句块中执行如变量初始化,打印输出表头等操作。

  2. 从文件或标准输入中读取一行,然后执行pattern{ commands }语句块。它逐行扫描文件,从第一行到最后一行重复这个过程,直到全部文件都被读取完毕。

    ​ pattern语句块:pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行。{ }类似一个循环体,会对文件中的每一行进行迭代,通常将变量初始化语句放在BEGIN语句块中,将打印结果等语句放在END语句块中。

  3. 当读至输入流末尾时,执行END { command }语句块。

    ​ END语句块:在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。

:awk 执行时,其浏览标记为$1,$2…$n,这种方法称为域标记。使用$1,$3 表示参照第 1 和第 3 域,注意这里使用逗号分隔域,使用$0 表示使用所有域。

AWK内置变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$n : 当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。
$0 : 这个变量包含执行过程中当前行的文本内容。
ARGC : 命令行参数的数目。
ARGIND : 命令行中当前文件的位置(从0开始算)。
ARGV : 包含命令行参数的数组。
CONVFMT : 数字转换格式(默认值为%.6g)。
ENVIRON : 环境变量关联数组。
ERRNO : 最后一个系统错误的描述。
FIELDWIDTHS : 字段宽度列表(用空格键分隔)。
FILENAME : 当前输入文件的名。
NR : 表示记录数,在执行过程中对应于当前的行号
FNR : 同NR :,但相对于当前文件。
FS : 字段分隔符(默认是任何空格)。
IGNORECASE : 如果为真,则进行忽略大小写的匹配。
NF : 表示字段数,在执行过程中对应于当前的字段数。 print $NF答应一行中最后一个字段
OFMT : 数字的输出格式(默认值是%.6g)。
OFS : 输出字段分隔符(默认值是一个空格)。
ORS : 输出记录分隔符(默认值是一个换行符)。
RS : 记录分隔符(默认是一个换行符)。
RSTART : 由match函数所匹配的字符串的第一个位置。
RLENGTH : 由match函数所匹配的字符串的长度。
SUBSEP : 数组下标分隔符(默认值是34)。

awk运算

  • 算术运算:(+,-,*,/,&,!,……,++,–)

所有用作算术运算符进行操作时,操作数自动转为数值,所有非数值都变为0

  • 赋值运算:(=, +=, -=,=,/=,%=,……=,*=)
  • 逻辑运算符: (||, &&)
  • 关系运算符:(<, <=, >,>=,!=, ==)
  • 正则运算符:(~,~!)(匹配正则表达式,与不匹配正则表达式)
1
2
awk 'BEGIN{a="100testa";if(a ~ /^100*/){print "ok";}}'
ok
条件操作符

<、<=、==、!=、>=、匹配正则表达式、!不匹配正则表达式

1
2
3
4
5
6
7
8
9
10
匹配: 	awk '{if ($4~/ASIMA/) print $0}' temp 表示如果第四个域包含 ASIMA,就打印整条
精确匹配: awk '$3=="48" {print $0}' temp 只打印第 3 域等于"48"的记录
不匹配: awk '$0 !~ /ASIMA/' temp 打印整条不包含 ASIMA 的记录
不等于: awk '$1 != "asima"' temp
小于: awk '{if ($1<$2) print $1 "is smaller"}' temp
设置大小写: awk '/[Gg]reen/' temp 打印整条包含 Green,或者 green 的记录
任意字符: awk '$1 ~/^...a/' temp 打印第 1 域中第四个字符是 a 的记录,符号’^’代表行首,符合’.’代表任意字符
或关系匹配: awk '$0~/(abc)|(efg)/' temp 使用|时,语句需要括起来
AND 与关系: awk '{if ( $1=="a" && $2=="b" ) print $0}' temp
OR 或关系: awk '{if ($1=="a" || $1=="b") print $0}' temp

文件操作

  • 打开文件 open(“filename”)
  • 关闭文件 close(“filename”)
  • 输出到文件 重定向到文件,如echo | awk ‘{printf(“hello word!n”) > “datafile”}’

将外部变量值传递给awk

  • 借助 -v 选项,可以将来自外部值(非stdin)传递给awk
1
2
VAR=10000
echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
  • 定义内部变量接收外部变量
1
2
3
var1="aaa"
var2="bbb"
echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2
  • 当输入来自文件时
1
awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename

数组

在awk中数组叫做关联数组(associative arrays)。awk 中的数组不必提前声明,也不必声明大小。数组元素用0或空字符串来初始化,这根据上下文而定。

1
2
3
4
5
6
7
8
9
10
11
awk '{print $0}' temp.txt > sav.txt       表示打印所有域并把结果重定向到 sav.txt 中
awk '{print $0}' temp.txt|tee sav.txt 和上例相似,不同的是将在屏幕上显示出来
awk '{print $1,$4}' temp.txt 只打印出第 1 和第 4 域
awk 'BEGIN {print "NAME GRADE\n----"} {print $1"\t"$4}' temp.txt
--表示打信息头,即输入的内容的第一行前加上"NAME GRADE\n-------------",同时内容以 tab 分开
awk 'BEGIN {print "being"} {print $1} END {print "end"}' temp 同时打印信息头和信息尾
awk -F :'{print $1,$4}' 使用‘:’来分割这一行,把这一行的第一第四个域打印出来 。
match(s,r) 测试s是否包含匹配r的字符串
字符转换: echo "65" |awk '{printf "%c\n",$0}' 输出A
awk 'BEGIN {printf "%f\n",999}' 输出999.000000
格式化输出:awk '{printf "%-15s %s\n",$1,$3}' temp 将第一个域全部左对齐显示
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
找一个实操文件:  cp /etc/passwd ~
打印2-5行: nl passwd | sed -n '2,5p'
打印奇数行: nl passwd | sed -n '1~2p'
"root" 全局替换为 "test",并只打印替换的那一行: sed -n 's/root/test/gp' passwd
找出 root 的那一行并计算行号: nl passwd | grep 'root'
将 root 替换为 www.root.com:

打印想要的数据列:

格式:awk '/匹配串/ {for(i=1;i<=NF;i++) if($i ~ /字符串/) print $i i}' $filename
awk '/匹配串/ {for(i=1;i<=NF;i++) if($i ~ /(字符串1|...|字符串n)/) print $i i}' $filename
例子:awk '/WEB_TILE/ {for(i=1;i<=NF;i++) if($i ~ /(双线|单线)/) print $i i}' t_config.txt

打印某个域后的所有域(已知域个数):【查看:cat file.txt】
awk '{for(i=4;i<=NF;i++) printf"%s ",$i} {print ""}' file.txt
X1 X2 X3
X1 X2 X3 X4 X5
X1 X2 X3 X4 X5 X6
这样的数据不够四行的,问题:将会打印出一个空行;在输出的结果中,每行结尾多了一个空格;
解决:
awk 'NF>4 {for (i=4;i<=NF;i++) {printf $i" "}printf "\n"}' file.txt
这个也行:cat file.txt |awk 'NF>4 {a=index($0,$4);print substr($0,a)}'

利用域值替换 //前3个域用字母a替换:
awk 'NF>4 { for(i=1;i<=3;i++){$i="a"}; print $0 }' file.txt

awk打印倒数第2列:
cat 1-iplist.txt | awk '{ print $(NF-2) }'|wc
awk -F":" '{print $2,$(NF),$(NF-1),$(NF-3)}' /etc/passwd

例如:我们需要查看 包含 sbin的进程 中的PID号:ps aux | grep sbin
只过滤出所有的PID号:ps aux | grep sbin | awk '{print $2}'
只获取前三行PID号: 【使用命令sed -n 指定行数;-n '2p':第二行;-n '1,3p':第一至三行】
ps aux | grep sbin | awk '{print $2}' | sed -n '1,3p'

打印文本文件的总行数 : awk 'END{print NR}' filename
打印文本第二行第一列 :sed -n "2, 1p" filename | awk 'print $1'

最后更新: 2020年05月10日 10:50

原始链接: https://yesong17.github.io/2020/03/09/awk%E7%94%A8%E6%B3%95/

× 请我吃大餐~
打赏二维码