安装新版本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.
Copyright (C) , - 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 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..tar.gz
[root@localhost ~]# cd gawk-4.2.
[root@localhost gawk-4.2.]# ./configure --prefix=/usr/local/gawk4. && make && make install
[root@localhost gawk-4.2.]# ln -fs /usr/local/gawk4./bin/gawk /usr/bin/awk
[root@localhost gawk-4.2.]# awk --version
[root@localhost gawk-4.2.]# gawk --version
此时,调用awk将调用新版本的gawk,调用gawk将调用旧版本的gawk

本系列的awk教程中,将大量使用到如下示例文件a.txt。

ID  name    gender  age  email          phone
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male ccc@xyz.com
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com

读取文件的几种方式

读取文件有如下几种常见的方式:

下面使用Shell的read命令来演示前4种读取文件的方式(第五种按字节数读取的方式read不支持)。

按字符数量读取

read的-n选项和-N选项可以指定一次性读取多少个字符。

[root@localhost ~]# read -n  data <a.txt     只读一个字符。
[root@localhost ~]# echo $data
I
[root@localhost ~]#
[root@localhost ~]# read -n data < a.txt 读100个字符,但如果不足100字符时遇到换行符则停止读取
[root@localhost ~]# echo $data
ID name gender age email phone
[root@localhost ~]# read -N data < a.txt
[root@localhost ~]# echo $data
ID name gender age email phone Bob male abc@qq.com
强制读取100字符,遇到换行符也不停止

如果按照字符数量读取,直到把文件读完,则使用while循环,且将文件放在while结构的后面,而不能放在while循环的条件位置:

[root@localhost ~]# while read -N  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
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male ccc@xyz.com
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com

一次性读整个文件                  

要一次性读取完整个文件,有两种方式:

  • 按照字符数量读取,且指定的字符数要大于文件的总大小
  • 按分隔符读取,且指定的分隔符是文件中不存在的字符,这样的话会一直读取,因为找不到分隔符而读完整个文件
[root@localhost ~]# read -N  data <a.txt
[root@localhost ~]# echo $data
ID name gender age email phone Bob male abc@qq.com Alice female def@gmail.com Tony male aaa@.com Kevin male bbb@.com Alex male ccc@xyz.com Andy female ddd@.com Jerry female exdsa@.com Peter male bax@qq.com Steven female bc@sohu.com Bruce female bcbd@.com
指定超出文件大小的字符数量
[root@localhost ~]# read -d "_" data <a.txt
[root@localhost ~]# echo $data
ID name gender age email phone Bob male abc@qq.com Alice female def@gmail.com Tony male aaa@.com Kevin male bbb@.com Alex male ccc@xyz.com Andy female ddd@.com Jerry female exdsa@.com Peter male bax@qq.com Steven female bc@sohu.com Bruce female bcbd@.com
指定文件中不存在的字符作为分隔符

awk用法入门              

[root@localhost ~]# awk 'awk_program' a.txt

awk示例:

#输出a.txt中的每一行
[root@localhost ~]# awk '{print $0}' a.txt
ID name gender age email phone
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male ccc@xyz.com
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com
# 多个代码块,代码块中多个语句
# 输出每行之后还输出两行:hello行和world行
[root@localhost ~]# awk '{print $0}{print "hello";print "world"}' a.txt
ID name gender age email phone
hello
world
Bob male abc@qq.com
hello
world
Alice female def@gmail.com
hello
world
Tony male aaa@.com
hello
world
Kevin male bbb@.com
hello
world
Alex male ccc@xyz.com
hello
world
Andy female ddd@.com
hello
world
Jerry female exdsa@.com
hello
world
Peter male bax@qq.com
hello
world
Steven female bc@sohu.com
hello
world
Bruce female bcbd@.com
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命令的执行过程:

  1. 读取文件第一行(awk默认按行读取文件)
  2. 将所读取的行赋值给awk的变量$0,于是$0中保存的就是本次所读取的行数据
  3. 进入代码块{print $0}并执行其中代码print $0,即输出$0,也即输出当前所读取的行
  4. 执行完本次代码之后,进入下一轮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
Bob male abc@qq.com
................................
[root@localhost ~]# awk 'END{print "我在后面"}{print $0}' a.txt
................................
Steven female bc@sohu.com
Bruce female bcbd@.com
我在后面
[root@localhost ~]# awk 'BEGIN{print "我在前面"}{print $0}END{print "我在后面"}' a.txt
我在前面
......................................
Bruce female bcbd@.com
我在后面

根据上面3行命令的执行结果,可总结出如下有关于BEGIN、END和main代码块的特性:

BEGIN代码块:

  • 在读取文件之前执行,且执行一次
  • 在BEGIN代码块中,无法使用$0或其它一些特殊变量

main代码块:

  • 读取文件时循环执行,(默认情况)每读取一行,就执行一次main代码块
  • main代码块可有多个

END代码块:

  • 在读取文件完成之后执行,且执行一次
  • 有END代码块,必有要读取的数据(可以是标准输入)
  • END代码块中可以使用$0等一些特殊变量,只不过这些特殊变量保存的是最后一轮awk循环的数据

awk命令行结构和语法结构              

awk命令行结构                            

awk [ -- ] program-text file ...        ()
awk -f program-file [ -- ] file ... ()
awk -e program-text [ -- ] file ... ()

其中:

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 # 即等值比较、大小比较 >{action}
pattern && pattern # 逻辑与 > && > {action}
pattern || pattern # 逻辑或 > || < {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="\n"}{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="\0":一次性读取所有数据,但有些特殊文件中包含了空字符\0
  • RS="^$":真正的一次性读取所有数据,因为非空文件不可能匹配成功
  • RS="\n+":按行读取,但忽略所有空行

示例:

按段落读取:RS=“”
[root@localhost ~]# awk 'BEGIN{RS=""}{print $0"------"}' a.txt
ID name gender age email phone
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male ccc@xyz.com
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com ------
一次性读取所有数据:RS='\0' RS="^$"
[root@localhost ~]# awk 'BEGIN{RS='\'}{print $0"------"}' a.txt
ID name gender age email phone
Bob male abc@qq.com ------
------ Alice female def@gmail.com ------
------ Tony male aaa@.com ------
------ Kevin male bbb@.com ------
------ Alex male ccc@xyz.com ------
------ Andy female ddd@.com ------ Jerry female exdsa@.com ------ Peter male ------
bax@qq.com
Steven female bc@sohu.com
------
Bruce female bcbd@.com ------ ------
[root@localhost ~]# awk 'BEGIN{RS="^$"}{print $0"------"}' a.txt
ID name gender age email phone
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male ccc@xyz.com
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com
------
忽略空行:RS='\n+'
[root@localhost ~]# awk 'BEGIN{RS="\n+"}{print $0"------"}' a.txt
ID name gender age email phone------
Bob male abc@qq.com ------
Alice female def@gmail.com ------
Tony male aaa@.com ------
Kevin male bbb@.com ------
Alex male ccc@xyz.com ------
Andy female ddd@.com ------
Jerry female exdsa@.com ------
Peter male bax@qq.com ------
Steven female bc@sohu.com ------
Bruce female bcbd@.com ------
忽略大小写:预定义变量IGNORECASE设置为非0值
[root@localhost ~]# awk 'BEGIN{IGNORECASE=1}{print $0"------"}' RS='[ab]' a.txt

预定义变量RT:

在awk每次读完一条记录时,会设置一个称为RT的预定义变量,表示Record Termination。

当RS为单个字符时,RT的值和RS的值是相同的。

当RS为多个字符(正则表达式)时,则RT设置为正则匹配到记录分隔符之后,真正用于划分记录时的字符。

当无法匹配到记录分隔符时,RT设置为控制空字符串(即默认的初始值)。

awk 'BEGIN{RS="(fe)?male"}{print RT}' a.txt

[root@localhost ~]# awk 'BEGIN{RS="(fe)?male"}{print RT}' a.txt
male
female
male
male
male
female
female
male
female
female

两种行号:NR和FNR                      

在读取每条记录之后,将其赋值给$0,同时还会设置NR、FNR、RT。

  • NR:所有文件的行号计数器
  • FNR:是各个文件的行号计数器
[root@localhost ~]# awk '{print NR}' a.txt a.txt

[root@localhost ~]# awk '{print FNR}' a.txt a.txt

详细分析awk字段分割              

awk读取每一条记录之后,会将其赋值给$0,同时还会对这条记录按照预定义变量FS划分字段,将划分好的各个字段分别赋值给$1 $2 $3 $4...$N,同时将划分的字段数量赋值给预定义变量NF

引用字段的方式                      

$N引用字段:

  • N=0:即$0,引用记录本身
  • 0<N<=NF:引用对应字段
  • N>NF:表示引用不存在的字段,返回空字符串
  • N<0:报错

可使用变量或计算的方式指定要获取的字段序号。

[root@localhost ~]# awk '{n = 5;print $n}' a.txt
[root@localhost ~]# awk '{print $(2+2)}' a.txt
[root@localhost ~]# awk '{print $(NF-3)}' a.txt
# 括号必不可少,用于改变优先级

读取record之后,将使用预定义变量FS、FIELDWIDTHS或FPAT中的一种来分割字段。分割完成之后,再进入main代码段(所以,在main中设置FS对本次已经读取的record是没有影响的,但会影响下次读取)。

划分字段方式(一):FS或-F                          

FS或者-F:字段分隔符

  • FS为单个字符时,该字符即为字段分隔符
  • FS为多个字符时,则采用正则表达式模式作为字段分隔符
  • 特殊的,也是FS默认的情况,FS为单个空格时,将以连续的空白(空格、制表符、换行符)作为字段分隔符
  • 特殊的,FS为空字符串""时,将对每个字符都进行分隔,即每个字符都作为一个字段
  • 设置预定义变量IGNORECASE为非零值,正则匹配时表示忽略大小写(只影响正则,所以FS为单字时无影响)
  • 如果record中无法找到FS指定的分隔符(例如将FS设置为"\n"),则整个记录作为一个字段,即$1$0相等
[root@localhost ~]# awk -F":" '{print $1}' /etc/passwd
[root@localhost ~]# awk 'BEGIN{FS=":"}{print $1}' /etc/passwd
字段分隔符指定为单个字符
[root@localhost ~]# awk 'BEGIN{FS=" +|@"}{print $1,$2,$3,$4,$5,$6}' a.txt
字段分隔符指定为正则表达式

划分字段方式(二):FIELDWIDTHS                      

指定预定义变量FIELDWIDTHS按字符宽度分割字段,这是gawk提供的高级功能。在处理某字段缺失时非常好用。

用法:

示例1:

没取完的字符串DDD被丢弃,且NF=
[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="2 3 2"}{print $1,$2,$3,$4}' <<<"AABBBCCDDDD"
AA BBB CC
字符串不够长度时无视
[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="2 3 2 100"}{print $1,$2,$3,$4"-"}' <<<"AABBBCCDDDD"
AA BBB CC DDDD-
*号取剩余所有,NF=
[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="2 3 *"}{print $1,$2,$3}' <<<"AABBBCCDDDD"
AA BBB CCDDDD
字段数多了,则取完字符串即可,NF=
[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="2 30 *"}{print $1,$2,NF}' <<<"AABBBCCDDDD"
AA BBBCCDDDD

示例2:处理某些字段缺失的数据。

如果按照常规的FS进行字段分割,则对于缺失字段的行和没有缺失字段的行很难统一处理,但使用FIELDWIDTHS则非常方便。

假设a.txt文本内容如下:

ID  name    gender  age  email          phone
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com

因为email字段有的是空字段,所以直接用FS划分字段不便处理。可使用FIELDWIDTHS。

# 字段1:4字符
# 字段2:8字符
# 字段3:8字符
# 字段4:2字符
# 字段5:先跳过3字符,再读13字符,该字段13字符
# 字段6:先跳过2字符,再读11字符,该字段11字符
[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="4 8 8 2 3:13 2:11"} NR>1{print "<"$1">","<"$2">","<"$3">","<"$4">","<"$5">","<"$6">"}' a1.txt
< > <Bob > <male > <> <abc@qq.com > <>
< > <Alice > <female > <> <def@gmail.com> <>
< > <Tony > <male > <> <aaa@.com > <>
< > <Kevin > <male > <> <bbb@.com > <>
< > <Alex > <male > <> < > <>
< > <Andy > <female > <> <ddd@.com > <>
< > <Jerry > <female > <> <exdsa@.com> <>
< > <Peter > <male > <> <bax@qq.com > <>
< > <Steven > <female > <> <bc@sohu.com > <>
< > <Bruce > <female > <> <bcbd@.com > <>
[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="4 8 8 2 3:13 2:11"} NR>1{if($5 ~ /^ +$/){print $0}}' a1.txt
Alex male
# 如果email为空,则输出它

划分字段方式(三):FPAT                      

FS是指定字段分隔符,来取得除分隔符外的部分作为字段。

FPAT是取得匹配的字符部分作为字段。它是gawk提供的一个高级功能。

FPAT根据指定的正则来全局匹配record,然后将所有匹配成功的部分组成$1、$2...,不会修改$0

  • awk 'BEGIN{FPAT="[0-9]+"}{print $3"-"}' a.txt
  • 之后再设置FS或FPAT,该变量将失效

FPAT常用于字段中包含了字段分隔符的场景。例如,CSV文件中的一行数据如下:

Robbins,Arnold,"1234 A Pretty Street, NE",MyTown,MyState,-,USA
[root@localhost ~]# awk 'BEGIN{FPAT="[0-9]+"}{print $3"-"}' a.txt
-
-
-
-
-
-
-
-
-
-
-

其中逗号分隔每个字段,但双引号包围的是一个字段整体,即使其中有逗号。

这时使用FPAT来划分各字段比使用FS要方便的多。

[root@localhost ~]# echo 'Robbins,Arnold,"1234 A Pretty Street, NE",MyTown,MyState,12345-6789,USA' | \
> awk ' \
> BEGIN{FPAT="[^,]*|(\"[^\"]*\")"} \
> { \
> for (i=;i<NF;i++){ \
> print "<"$i">"
> } \
> }'
<Robbins>
<Arnold>
<"1234 A Pretty Street, NE">
<MyTown>
<MyState>
<->

最后,patsplit()函数和FPAT的功能一样。

检查字段划分的方式                          

有FS、FIELDWIDTHS、FPAT三种获取字段的方式,可使用PROCINFO数组来确定本次使用何种方式获得字段。

PROCINFO是一个数组,记录了awk进程工作时的状态信息。

如果:

  • PROCINFO["FS"]=="FS",表示使用FS分割获取字段
  • PROCINFO["FPAT"]=="FPAT",表示使用FPAT匹配获取字段
  • PROCINFO["FIELDWIDTHS"]=="FIELDWIDTHS",表示使用FIELDWIDTHS分割获取字段

例如:

if(PROCINFO["FS"]=="FS"){
...FS spliting...
} else if(PROCINFO["FPAT"]=="FPAT"){
...FPAT spliting...
} else if(PROCINFO["FIELDWIDTHS"]=="FIELDWIDTHS"){
...FIELDWIDTHS spliting...
}

修改字段或NF值的联动效应              

注意下面的分割和计算两词:分割表示使用FS(field Separator),计算表示使用预定义变量OFS(Output Field Separator)。

  1. 修改$0,将使用FS重新分割字段,所以会影响$1、$2...
  2. 修改$1、$2,将根据$1$NF等各字段来重新计算$0
    • 即使是$1 = $1这样的原值不变的修改,也一样会重新计算$0
  3. 为不存在的字段赋值,将新增字段并按需使用空字符串填充中间的字段,并使用OFS重新计算$0
    • awk 'BEGIN{OFS="-"}{$(NF+2)=5;print $0}' a.txt
  4. 增加NF值,将使用空字符串新增字段,并使用OFS重新计算$0
    • awk 'BEGIN{OFS="-"}{NF+=3;print $0}' a.txt
  5. 减小NF值,将丢弃一定数量的尾部字段,并使用OFS重新计算$0
    • awk 'BEGIN{OFS="-"}{NF-=3;print $0}' a.txt
[root@localhost ~]# awk 'BEGIN{OFS="-"}{$(NF+2)=5;print $0}' a.txt
ID-name-gender-age-email-phone--
-Bob-male--abc@qq.com---
-Alice-female--def@gmail.com---
-Tony-male--aaa@.com---
-Kevin-male--bbb@.com---
-Alex-male--ccc@xyz.com---
-Andy-female--ddd@.com---
-Jerry-female--exdsa@.com---
-Peter-male--bax@qq.com---
-Steven-female--bc@sohu.com---
-Bruce-female--bcbd@.com---
[root@localhost ~]# awk 'BEGIN{OFS="-"}{NF+=3;print $0}' a.txt
ID-name-gender-age-email-phone---
-Bob-male--abc@qq.com----
-Alice-female--def@gmail.com----
-Tony-male--aaa@.com----
-Kevin-male--bbb@.com----
-Alex-male--ccc@xyz.com----
-Andy-female--ddd@.com----
-Jerry-female--exdsa@.com----
-Peter-male--bax@qq.com----
-Steven-female--bc@sohu.com----
-Bruce-female--bcbd@.com----
[root@localhost ~]# awk 'BEGIN{OFS="-"}{NF-=3;print $0}' a.txt
ID-name-gender
-Bob-male
-Alice-female
-Tony-male
-Kevin-male
-Alex-male
-Andy-female
-Jerry-female
-Peter-male
-Steven-female
-Bruce-female

关于$0                

当读取一条record之后,将原原本本地被保存到$0当中。

awk '{print $0}' a.txt

换句话说,没有导致$0重建,$0就一直是原原本本的数据,所以指定OFS也无效。

[root@localhost ~]# awk 'BEGIN{OFS="-"}{print $0}' a.txt      # OFS此处无效
ID name gender age email phone
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male ccc@xyz.com
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com

$0重建后,将自动使用OFS重建,所以即使没有指定OFS,它也会采用默认值(空格)进行重建。

[root@localhost ~]# awk '{$1=$1;print $0}'  a.txt   输出时将以空格分隔各字段
ID name gender age email phone
Bob male abc@qq.com
Alice female def@gmail.com
Tony male aaa@.com
Kevin male bbb@.com
Alex male ccc@xyz.com
Andy female ddd@.com
Jerry female exdsa@.com
Peter male bax@qq.com
Steven female bc@sohu.com
Bruce female bcbd@.com

[root@localhost ~]# awk '{print $0;$1=$1;print $0}' OFS="-" a.txt
ID name gender age email phone
ID-name-gender-age-email-phone
1 Bob male 28 abc@qq.com 18023394012
1-Bob-male-28-abc@qq.com-18023394012
2 Alice female 24 def@gmail.com 18084925203
2-Alice-female-24-def@gmail.com-18084925203
3 Tony male 21 aaa@163.com 17048792503
3-Tony-male-21-aaa@163.com-17048792503
4 Kevin male 21 bbb@189.com 17023929033
4-Kevin-male-21-bbb@189.com-17023929033
5 Alex male 18 ccc@xyz.com 18185904230
5-Alex-male-18-ccc@xyz.com-18185904230
6 Andy female 22 ddd@139.com 18923902352
6-Andy-female-22-ddd@139.com-18923902352
7 Jerry female 25 exdsa@189.com 18785234906
7-Jerry-female-25-exdsa@189.com-18785234906
8 Peter male 20 bax@qq.com 17729348758
8-Peter-male-20-bax@qq.com-17729348758
9 Steven female 23 bc@sohu.com 15947893212
9-Steven-female-23-bc@sohu.com-15947893212
10 Bruce female 27 bcbd@139.com 13942943905
10-Bruce-female-27-bcbd@139.com-13942943905

如果重建$0之后,再去修改OFS,将对当前行无效,但对之后的行有效。所以如果也要对当前行生效,需要再次重建。

[root@localhost ~]# awk '{$4+=10;OFS="-";print $0}' a.txt OFS对第一行无效
ID name gender email phone
-Bob-male--abc@qq.com-
-Alice-female--def@gmail.com-
-Tony-male--aaa@.com-
-Kevin-male--bbb@.com-
-Alex-male--ccc@xyz.com-
-Andy-female--ddd@.com-
-Jerry-female--exdsa@.com-
-Peter-male--bax@qq.com-
-Steven-female--bc@sohu.com-
-Bruce-female--bcbd@.com-
对所有行有效
[root@localhost ~]# awk '{$4+=10;OFS="-";$1=$1;print $0}' a.txt
ID-name-gender--email-phone
-Bob-male--abc@qq.com-
-Alice-female--def@gmail.com-
-Tony-male--aaa@.com-
-Kevin-male--bbb@.com-
-Alex-male--ccc@xyz.com-
-Andy-female--ddd@.com-
-Jerry-female--exdsa@.com-
-Peter-male--bax@qq.com-
-Steven-female--bc@sohu.com-
-Bruce-female--bcbd@.com-

关注$0重建是一个非常有用的技巧。

例如,下面通过重建$0的技巧来实现去除行首行尾空格并压缩中间空格:

[root@localhost ~]# echo "   a  b  c   d   " | awk '{$1=$1;print}'
a b c d
[root@localhost ~]# echo " a b c d " | awk '{$1=$1;print}' OFS="-"
a-b-c-d

awk数据筛选示例                

筛选行                  

# .根据行号筛选
awk 'NR==2' a.txt # 筛选出第二行
awk 'NR>=2' a.txt # 输出第2行和之后的行
# .根据正则表达式筛选整行
awk '/qq.com/' a.txt # 输出带有qq.com的行
awk '$0 ~ /qq.com/' a.txt # 等价于上面命令
awk '/^[^@]+$/' a.txt # 输出不包含@符号的行
awk '!/@/' a.txt # 输出不包含@符号的行
# .根据字段来筛选行
awk '($4+0) > 24{print $0}' a.txt # 输出第4字段大于24的行
awk '$5 ~ /qq.com/' a.txt # 输出第5字段包含qq.com的行
# .将多个筛选条件结合起来进行筛选
awk 'NR>=2 && NR<=7' a.txt
awk '$3=="male" && $6 ~ /^170/' a.txt
awk '$3=="male" || $6 ~ /^170/' a.txt
# .按照范围进行筛选 flip flop
# pattern1,pattern2{action}
awk 'NR==2,NR==7' a.txt # 输出第2到第7行
awk 'NR==2,$6 ~ /^170/' a.txt

处理字段                    

从ifconfig命令的结果中筛选出除了lo网卡外的所有IPv4地址。

[root@localhost ~]# ifconfig | awk '/inet / && !($2 ~ /^127/){print $2}'
192.168.1.205

awk工作流程              

参考自:man awk的"AWK PROGRAM EXECUTION"段。

man --pager='less -p ^"AWK PROGRAM EXECUTION"' awk

执行步骤

精通awk系列的更多相关文章

  1. 精通awk系列文章

    精通awk系列文章 我录制了两个awk相关的视频教程: Awk经典实战案例精讲 精通awk精品课程:awk从入门到精通 1.安装新版本的gawk 2.本教程测试所用示例文件 3.铺垫知识:读取文件的几 ...

  2. 精通awk系列(8):awk划分字段的3种方式

    回到: Linux系列文章 Shell系列文章 Awk系列文章 详细分析awk字段分割 awk读取每一条记录之后,会将其赋值给$0,同时还会对这条记录按照预定义变量FS划分字段,将划分好的各个字段分别 ...

  3. 精通awk系列(16):gawk支持的正则表达式

    回到: Linux系列文章 Shell系列文章 Awk系列文章 gawk支持的正则 . # 匹配任意字符,包括换行符 ^ $ [...] [^...] | + * ? () {m} {m,} {m,n ...

  4. 精通awk系列(14):细说awk中的变量和变量赋值

    回到: Linux系列文章 Shell系列文章 Awk系列文章 awk变量 awk的变量是动态变量,在使用时声明. 所以awk变量有3种状态: 未声明状态:称为untyped类型 引用过但未赋值状态: ...

  5. 精通awk系列(13):print、printf、sprintf和重定向

    回到: Linux系列文章 Shell系列文章 Awk系列文章 输出操作 awk可以通过print.printf将数据输出到标准输出或重定向到文件. print print elem1,elem2,e ...

  6. 精通awk系列(15):awk数据类型和字面量

    回到: Linux系列文章 Shell系列文章 Awk系列文章 数据类型 gawk有两种基本的数据类型:数值和字符串.在gawk 4.2.0版本中,还支持第三种基本的数据类型:正则表达式类型. 数据是 ...

  7. 精通awk系列(12):awk getline用法详解

    回到: Linux系列文章 Shell系列文章 Awk系列文章 getline用法详解 除了可以从标准输入或非选项型参数所指定的文件中读取数据,还可以使用getline从其它各种渠道获取需要处理的数据 ...

  8. 精通awk系列(11):awk的工作流程

    回到: Linux系列文章 Shell系列文章 Awk系列文章 awk工作流程 参考自:man awk的"AWK PROGRAM EXECUTION"段. man --pager= ...

  9. 精通awk系列(10):awk筛选行和处理字段的示例

    回到: Linux系列文章 Shell系列文章 Awk系列文章 awk数据筛选示例 筛选行 # 1.根据行号筛选 awk 'NR==2' a.txt # 筛选出第二行 awk 'NR>=2' a ...

  10. 精通awk系列(9):修改字段或NF引起的$0重新计算

    回到: Linux系列文章 Shell系列文章 Awk系列文章 修改字段或NF值的联动效应 注意下面的分割和计算两词:分割表示使用FS(field Separator),计算表示使用预定义变量OFS( ...

随机推荐

  1. day82_10_31celery的使用

    1.缓存 当一些数据需要固定地且频繁访问数据库时,需要使用到接口缓存. 以轮播图为例,每个用户都会访问首页,首页的轮播图长时间不会改变,所以可以使用cache,将固定数据保存到缓存中(redis),第 ...

  2. UVA11374 Airport Express 正反两次最短路

    问题描述 洛谷(有翻译) 吐槽 一道坑题. 如何对待商务票 因为商务票只有一张,所以在\(k\)条边中只有一条边会被选中,很显然,最后这条边会被枚举. 如何选择使用商务票的边 假设我们正在枚举这条边, ...

  3. 工具资源系列之给 windows 装个 vmware 虚拟机

    如果长时间处于同一种环境,慢慢得我们会觉得有些无聊,所以适当地出去走走看看外面的世界能带给我们不一样的体验. 所以,何不出去走走,看看另一个世界? 然而,平时需要工作很难抽身无所顾忌地潇洒走开,这是不 ...

  4. NOIP201310华容道

    题目描述 Description 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时 ...

  5. 基础知识 wps去广告

    原文:http://www.360doc.com/content/19/0618/15/38017100_843312032.shtml 原文:http://wps.crcc.cn/

  6. 最小费用最大流 学习笔记&&Luogu P3381 【模板】最小费用最大流

    题目描述 给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 题目链接 思路 最大流是没有问题的,关键是同时保证最小费用,因此,就可以把 ...

  7. 小米笔记本pro 黑苹果系统无法进入系统,频繁重启故障解决记录

    问题1:频繁重启,然后clover丢失 表现情况:开机没有选择macos 或windos的界面 解决办法:进入windows使用工具easyefi,直接添加一个clover start boot,选择 ...

  8. PCL学习之:将超声数据按照PCL点云方式发布出去

    前言:基于2D激光雷达的机器人,想让它跑自动导航,众所周知有2个比较明显的缺陷,1,那就是普通的激光雷达无法对玻璃或是镜面物体有反映; 2,机器人避障时只能对某一个平面的物体有反映,超过或者低于这个平 ...

  9. oracle简单查询单词

    单词 释义 select 查询 from 从... where 条件查询 as 别名 not 取反 and 和 or 或者 between...and... 范围取值,包含边界 like 模糊查询 e ...

  10. CompletableFuture3

    public class CompletableFuture3 { public static void main(String[] args) throws ExecutionException, ...