安装新版本gawk
awk有很多种版本,例如nawk、gawk。gawk是GNU awk,它的功能很丰富。
本教程采用的是gawk 4.2.0版本,4.2.0版本的gawk是一个比较大的改版,新支持的一些特性非常好用,而在低于4.2.0版本时这些语法可能会报错。所以,请先安装4.2.0版本或更高版本的gawk。
查看awk版本
[root@localhost ~]# awk --version GNU Awk 4.0.2 Copyright (C) 1989, 1991-2012 Free Software Foundation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
这里以安装gawk 4.2.0为例。
[root@localhost ~]# wget --no-check-certificate https://mirrors.tuna.tsinghua.edu.cn/gnu/gawk/gawk-4.2.0.tar.gz [root@localhost ~]# tar -zxf gawk-4.2.0.tar.gz [root@localhost ~]# cd gawk-4.2.0 [root@localhost gawk-4.2.0]# ./configure --prefix=/usr/local/gawk4.2 && make && make install [root@localhost gawk-4.2.0]# ln -fs /usr/local/gawk4.2/bin/gawk /usr/bin/awk [root@localhost gawk-4.2.0]# awk --version [root@localhost gawk-4.2.0]# gawk --version 此时,调用awk将调用新版本的gawk,调用gawk将调用旧版本的gawk
本系列的awk教程中,将大量使用到如下示例文件a.txt。
ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 2 Alice female 24 def@gmail.com 18084925203 3 Tony male 21 aaa@163.com 17048792503 4 Kevin male 21 bbb@189.com 17023929033 5 Alex male 18 ccc@xyz.com 18185904230 6 Andy female 22 ddd@139.com 18923902352 7 Jerry female 25 exdsa@189.com 18785234906 8 Peter male 20 bax@qq.com 17729348758 9 Steven female 23 bc@sohu.com 15947893212 10 Bruce female 27 bcbd@139.com 13942943905
读取文件的几种方式
读取文件有如下几种常见的方式:
下面使用Shell的read命令来演示前4种读取文件的方式(第五种按字节数读取的方式read不支持)。
按字符数量读取
read的-n选项和-N选项可以指定一次性读取多少个字符。
[root@localhost ~]# read -n 1 data <a.txt 只读一个字符。 [root@localhost ~]# echo $data I [root@localhost ~]# [root@localhost ~]# read -n 100 data < a.txt 读100个字符,但如果不足100字符时遇到换行符则停止读取 [root@localhost ~]# echo $data ID name gender age email phone [root@localhost ~]# read -N 100 data < a.txt [root@localhost ~]# echo $data ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 2 强制读取100字符,遇到换行符也不停止
如果按照字符数量读取,直到把文件读完,则使用while循环,且将文件放在while结构的后面,而不能放在while循环的条件位置:
[root@localhost ~]# while read -N 3 data;do > echo "$data" > done <a.txt
按分隔符读取
read命令的-d选项可以指定读取文件时的分隔符。
[root@localhost ~]# read -d "m" data <a.txt [root@localhost ~]# echo $data ID na 一直读取,直到遇到字符m才停止,并将读取的数据保存到data变量中
如果要按分隔符读取并读完整个文件,则使用while循环:
[root@localhost ~]# while read -d "m" data ;do > echo "$data" > done <a.txt
按行读取
read默认情况下就是按行读取的,一次读取一行。
[root@localhost ~]# read line <a.txt [root@localhost ~]# echo $line ID name gender age email phone
如果要求按行读取完整个文件,则使用while循环:
[root@localhost ~]# while read line;do > echo "$line" > done <a.txt ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 2 Alice female 24 def@gmail.com 18084925203 3 Tony male 21 aaa@163.com 17048792503 4 Kevin male 21 bbb@189.com 17023929033 5 Alex male 18 ccc@xyz.com 18185904230 6 Andy female 22 ddd@139.com 18923902352 7 Jerry female 25 exdsa@189.com 18785234906 8 Peter male 20 bax@qq.com 17729348758 9 Steven female 23 bc@sohu.com 15947893212 10 Bruce female 27 bcbd@139.com 13942943905
一次性读整个文件
要一次性读取完整个文件,有两种方式:
- 按照字符数量读取,且指定的字符数要大于文件的总大小
- 按分隔符读取,且指定的分隔符是文件中不存在的字符,这样的话会一直读取,因为找不到分隔符而读完整个文件
[root@localhost ~]# read -N 1000000 data <a.txt [root@localhost ~]# echo $data ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 2 Alice female 24 def@gmail.com 18084925203 3 Tony male 21 aaa@163.com 17048792503 4 Kevin male 21 bbb@189.com 17023929033 5 Alex male 18 ccc@xyz.com 18185904230 6 Andy female 22 ddd@139.com 18923902352 7 Jerry female 25 exdsa@189.com 18785234906 8 Peter male 20 bax@qq.com 17729348758 9 Steven female 23 bc@sohu.com 15947893212 10 Bruce female 27 bcbd@139.com 13942943905 指定超出文件大小的字符数量 [root@localhost ~]# read -d "_" data <a.txt [root@localhost ~]# echo $data ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 2 Alice female 24 def@gmail.com 18084925203 3 Tony male 21 aaa@163.com 17048792503 4 Kevin male 21 bbb@189.com 17023929033 5 Alex male 18 ccc@xyz.com 18185904230 6 Andy female 22 ddd@139.com 18923902352 7 Jerry female 25 exdsa@189.com 18785234906 8 Peter male 20 bax@qq.com 17729348758 9 Steven female 23 bc@sohu.com 15947893212 10 Bruce female 27 bcbd@139.com 13942943905 指定文件中不存在的字符作为分隔符
awk用法入门
[root@localhost ~]# awk 'awk_program' a.txt
awk示例:
#输出a.txt中的每一行 [root@localhost ~]# awk '{print $0}' a.txt ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 2 Alice female 24 def@gmail.com 18084925203 3 Tony male 21 aaa@163.com 17048792503 4 Kevin male 21 bbb@189.com 17023929033 5 Alex male 18 ccc@xyz.com 18185904230 6 Andy female 22 ddd@139.com 18923902352 7 Jerry female 25 exdsa@189.com 18785234906 8 Peter male 20 bax@qq.com 17729348758 9 Steven female 23 bc@sohu.com 15947893212 10 Bruce female 27 bcbd@139.com 13942943905 # 多个代码块,代码块中多个语句 # 输出每行之后还输出两行:hello行和world行 [root@localhost ~]# awk '{print $0}{print "hello";print "world"}' a.txt ID name gender age email phone hello world 1 Bob male 28 abc@qq.com 18023394012 hello world 2 Alice female 24 def@gmail.com 18084925203 hello world 3 Tony male 21 aaa@163.com 17048792503 hello world 4 Kevin male 21 bbb@189.com 17023929033 hello world 5 Alex male 18 ccc@xyz.com 18185904230 hello world 6 Andy female 22 ddd@139.com 18923902352 hello world 7 Jerry female 25 exdsa@189.com 18785234906 hello world 8 Peter male 20 bax@qq.com 17729348758 hello world 9 Steven female 23 bc@sohu.com 15947893212 hello world 10 Bruce female 27 bcbd@139.com 13942943905 hello world
对于awk '{print $0}' a.txt
,它类似于shell的while循环while read line;do echo "$line";done <a.txt
。awk隐藏了读取每一行的while循环,它会自动读取每一行,其中的{print $0}
对应于Shell的while循环体echo "$line"
部分。
下面再分析该awk命令的执行过程:
- 读取文件第一行(awk默认按行读取文件)
- 将所读取的行赋值给awk的变量
$0
,于是$0
中保存的就是本次所读取的行数据 - 进入代码块
{print $0}
并执行其中代码print $0
,即输出$0
,也即输出当前所读取的行 - 执行完本次代码之后,进入下一轮awk循环:继续读取下一行(第二行)
- 将第二行赋值给变量
$0
- 进入代码块执行
print $0
- 执行完代码块后再次进入下一轮awk循环,即读取第三行,然后赋值给
$0
,再执行代码块 - ...不断循环,直到读完文件所有数据...
- 将第二行赋值给变量
5.退出awk
BEGIN和END语句块
awk的所有代码(目前这么认为)都是写在语句块中的。
例如:
awk '{print $0}' a.txt awk '{print $0}{print $0;print $0}' a.txt
每个语句块前面可以有pattern,所以格式为:
pattern1{statement1}pattern2{statement3;statement4;...}
语句块可分为3类:BEGIN语句块、END语句块和main语句块。其中BEGIN语句块和END语句块都是的格式分别为BEGIN{...}
和END{...}
,而main语句块是一种统称,它的pattern部分没有固定格式,也可以省略,main代码块是在读取文件的每一行的时候都执行的代码块。
分析下面三个awk命令的执行结果
[root@localhost ~]# awk 'BEGIN{print "我在前面"}{print $0}' a.txt 我在前面 ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 ................................ [root@localhost ~]# awk 'END{print "我在后面"}{print $0}' a.txt ................................ 9 Steven female 23 bc@sohu.com 15947893212 10 Bruce female 27 bcbd@139.com 13942943905 我在后面 [root@localhost ~]# awk 'BEGIN{print "我在前面"}{print $0}END{print "我在后面"}' a.txt 我在前面 ...................................... 10 Bruce female 27 bcbd@139.com 13942943905 我在后面
根据上面3行命令的执行结果,可总结出如下有关于BEGIN、END和main代码块的特性:
BEGIN代码块:
- 在读取文件之前执行,且执行一次
- 在BEGIN代码块中,无法使用
$0
或其它一些特殊变量
main代码块:
- 读取文件时循环执行,(默认情况)每读取一行,就执行一次main代码块
- main代码块可有多个
END代码块:
- 在读取文件完成之后执行,且执行一次
- 有END代码块,必有要读取的数据(可以是标准输入)
- END代码块中可以使用
$0
等一些特殊变量,只不过这些特殊变量保存的是最后一轮awk循环的数据
awk命令行结构和语法结构
awk命令行结构
awk [ -- ] program-text file ... (1) awk -f program-file [ -- ] file ... (2) awk -e program-text [ -- ] file ... (3)
其中:
awk语法结构
awk语法结构即awk代码部分的结构。
awk的语法充斥着pattern{action}
的模式,它们称为awk rule。
例如:
[root@localhost ~]# awk '/^[0-9]/{$1>5;print $1} /Alice/{print "Alice"} END{print "hello"}' a.txt
上面示例中,有BEGIN语句块,有END语句块,还有2个main代码块,两个main代码块都使用了正则表达式作为pattern。
关于awk的语法:
- 多个
pattern{action}
可以直接连接连用 - action中多个语句如果写在同一行,则需使用分号分隔
- pattern部分用于筛选行,action表示在筛选通过后执行的操作
- pattern和action都可以省略
- 省略
pattern
,等价于对每一行数据都执行action- 例如:
awk '{print $0}' a.txt
- 例如:
- 省略代码块
{action}
,等价于{print}
即输出所有行- 例如:
awk '/Alice/' a.txt
等价于awk '/Alice/{print $0}' a.txt
- 例如:
- 省略代码块中的
action
,表示对筛选的行什么都不做- 例如:
awk '/Alice/{}' a.txt
- 例如:
pattern{action}
任何一部分都可以省略- 例如:
awk '' a.txt
- 例如:
- 省略
pattern和action
对于pattern{action}
语句结构(都称之为语句块),其中的pattern部分可以使用下面列出的模式:
# 特殊pattern BEGIN END # 布尔代码块 /regular expression/ # 正则匹配成功与否 /a.*ef/{action} relational expression # 即等值比较、大小比较 3>2{action} pattern && pattern # 逻辑与 3>2 && 3>1 {action} pattern || pattern # 逻辑或 3>2 || 3<1 {action} ! pattern # 逻辑取反 !/a.*ef/{action} (pattern) # 改变优先级 pattern ? pattern : pattern # 三目运算符决定的布尔值 # 范围pattern,非布尔代码块 pattern1, pattern2 # 范围,pat1打开、pat2关闭,即flip,flop模式
action部分,可以是任何语句,例如print。
详细分析awk如何读取文件
wk读取输入文件时,每次读取一条记录(record)(默认情况下按行读取,所以此时记录就是行)。每读取一条记录,将其保存到$0
中,然后执行一次main代码段。
awk '{print $0}' a.txt
如果是空文件,则因为无法读取到任何一条记录,将导致直接关闭文件,而不会进入main代码段。
[root@localhost ~]# touch x.log 创建一个空文件 [root@localhost ~]# awk '{print "hello world"}' x.log
可设置表示输入记录分隔符的预定义变量RS(Record Separator)来改变每次读取的记录模式。
[root@localhost ~]# awk 'BEGIN{RS=" "}{print $0}' a.txt [root@localhost ~]# awk 'BEGIN{RS="m"}{print $0}' a.txt
RS通常设置在BEGIN代码块中,因为要先于读取文件就确定好RS分隔符。
RS指定输入记录分隔符时,所读取的记录中是不包含分隔符字符的。例如RS="a"
,则$0
中一定不可能出现字符a。
特殊的RS值用来解决特殊读取需求:
RS=""
:按段落读取RS=" "
:一次性读取所有数据,但有些特殊文件中包含了空字符