您的位置:首页 > 运维架构 > Linux

Linux文本处理三剑客之awk

2017-06-25 15:03 393 查看

awk简介

awk是一种解释执行的编程语言,用来专门处理文本数据,其名称是由它们设计者的名字缩写而来 ———
Afred Aho
,
Peter Weinberger
Brian Kernighan
。常见版本有:

-
awk
: 最原初的版本,它由 AT&T 实验室开发

-
nawk
:awk的改进增强版本

-
gawk
:GNU awk,所有的 GNU/Linux 发行版都包括gawk,且完全兼容awk与nawk

工作原理

awk -v FS=”:” -v OFS=”,” ‘{print 1,3}’ /etc/passwd

1.awk对文件或管道的内容一次只处理一行,将获取搭配的这一行赋值给内部变量$0

2.将取到的一行的内容按awk内部变量FS定义的分割符(缺省为空格,包含tab制表符,上面的例子通过
-v
选项指定分解符为
为分隔符)分为多个字段,每一段存储在1,2…开始的变量中

3.awk中print命令打印字段;{print 1,3} 只取有用的第一段和第三段;在打印时1和3之间由空格间隔。“,”逗号是一个映射到内部的输出字段分隔符(OFS),OFS变量缺省为空格,例子中指定
为输出分隔符。接下来,awk处理下一行数据,直到所有的行处理完。

语法

awk [option] program FILE1...


program:
PATTERN{ACTION STATEMENTS}


字段说明:

option
:常用选项有
-F
-v


program
:匹配并操作文本

-
{action}
:每一次输入的行都会执行一次主体部分的命令

-
PATTERN
:限定对于输入的行的规则,可选

以下详细说明awk的各个字段…

用法

option

-F
:指明输入时用到的字段分隔符,默认为空白字符

-
v var=value
:自定义变量

-f
:读取文件中的awk规则

awk -v str='hello' '{print str,$1}' file1
awk 'BEGIN{str="hello"}{print str,$1}' file1    # 效果同上
awk -f file1                                    # awk规则写到脚本中读取


program

1.PATTERN

empty
:空模式,匹配每一行

/regular expression/
:仅处理能够被此处的模式匹配到的行

relational expression
: 关系表达式;结果有“真”有“假”;结果为“真”才会被处理;真:结果为非0值,非空字符串;

line ranges
:行范围

startline,endline:/pat1/,/pat2/
#指明起始行结束行模式 note: 不支持直接给出数字的格式

BEGIN/END
模式

BEGIN
{}: 仅在开始处理文件中的文本之前执行一次

END
{}:仅在文本处理完成之后执行一次

awk '/UUID/{print $1}' /etc/fstab   #处理匹配到UUID的行
awk '!/UUID/{print $1}' /etc/fstab  #处理UUID以外的行
awk -F : '$3<=500{print $1}' /etc/passwd  #关系表达式匹配

awk -F : '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd       #关系表达是用法
awk -F : '$NF~/bash$/{print $1,$NF}' /etc/passwd            #用模式匹配符匹配
awk -F : '/root/,/ftp/{print $1,$NF}' /etc/passwd
awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd


2.常用action

print

print item1, item2, ...


要点:

- 逗号分隔符;

- 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式;

- 如省略item,相当于print $0;

awk '{print "line one\nline two\nline three"}'


printf

格式化输出:
printf FORMAT, item1, item2, ...


要点:

- FORMAT必须给出

- 不会自动换行,需要显式给出换行控制符,\n

- FORMAT中需要分别为后面的每个item指定一个格式化符号

格式符:

%c
: 显示字符的ASCII码

%d, %i
: 显示十进制整数

%e, %E
: 科学计数法数值显示

%f
:显示为浮点数

%g, %G
:以科学计数法或浮点形式显示数值

%s
:显示字符串

%u
:无符号整数

%%
: 显示%自身

修饰符:

#[.#]
:第一个数字控制显示的宽度;第二个#表示小数点后的精度;

-
: 左对齐

+
:显示数值的符号

awk -F: '{printf "%s\n",$1}' /etc/passwd
awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
awk -F : '{printf "Username:%15s\n",$1}' /etc/passwd  #用修饰符控制输出字符的宽度
awk -F : '{printf "Username:%15s\n",$1}' /etc/passwd  #左对齐


操作符

算术操作符:X
+
,
-
,
*
,
/
,
%
,
**
,
^
Y (X^Y Y为X的次方)

赋值操作符:X
=
,
+=
,
-=
,
*=
,
/=
,
%=
,
**=
,
^=
, Y
++
,
--


比较操作符:X
>
,
>=
,
<
,
<=
,
==
,
!=
,
~
,
!~
Y

逻辑关系符:X
&&
,
||
Y

三元运算符:selector
?
if-true-exp
:
if-false-exp

#输出系统用户
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd


条件控制

if-else: if (condition) {then-body} else {[ else-body ]}

awk -F: '{if ($1=="root") print $1, "Admin"; else print $1, "Common User"}' /etc/passwd
awk -F: '{if ($1=="root") printf "%-15s: %s\n", $1,"Admin"; else printf "%-15s: %s\n", $1, "Common User"}' /etc/passwd
awk -F: -v sum=0 '{if ($3>=500) sum++}END{print sum}' /etc/passwd


while:while (condition){statement1; statment2; …}

awk -F: '{i=1;while (i<=3) {print $i;i++}}' /etc/passwd
awk -F: '{i=1;while (i<=NF) { if (length($i)>=4) {print $i}; i++ }}' /etc/passwd


do-while:do {statement1, statement2, …} while (condition)

awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd


for:

for ( variable assignment; condition; iteration process) { statement1, statement2, ...}


for (i in array) {statement1, statement2, ...}


awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd
awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd
awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd


case:
switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}


awk 'BEGIN{foo=0;switch(foo){case 0:print "yes";break; case 1:print "no";break; default: print "unknow"}}'


next:提前结束对本行文本的处理,并接着处理下一行

awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd    # 显示其ID号为奇数的用户


awk高级应用

内置变量

$0
:匹配到的行的全部字段

$1~$#
:匹配到行分割后的指定字段

NF
:number of field,字段个数

FS
:input field seperator,输入的分隔符,默认为空白字符

OFS
:output field seperator,输出的分隔符,默认为空白字符

RS
:input record seperator,输入的换行符

ORS
:output record seperator,输出时的换行符

NR
:number of record,当前记录的字段数,如果多文件时编号累加

FNR
:与NR不同的是,FNR用于记录正处理的行是当前这一文件中被总共处理的行数

FILENAME
:awk命令所处理的文件的名称

数组

array[index]


index可以使用任意字符串,当某数据组元素不存在时,要引用其时,awk会自动创建此元素并初始化为空串,因此,要判断某数据组中是否存在某元素,需要使用index in array的方式。

- 遍历:for (var in array) { statement1, … }

- 删除数组变量:delete array[index]

#每出现一被/^tcp/模式匹配到的行,数组S[$NF]就加1,NF为当前匹配到的行的最后一个字段,此处用其值做为数组S的元素索引;
netstat -ant | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

#统计某日志文件中IP地的访问量
awk '{counts[$1]++}; END {for(url in counts) print counts:指定日期时间戳,注意加引号

strftime(format [,timestamp [,utc-flage]])
:生成指定格式时间,具体格式参考date命令

tolower(str)
:转为小写

toupper(str)
:转为大写

awk 'BEGIN{str="this is a test";split(str,A," ");print length(A);for(i in A){print i,A[i];}}'
awk 'BEGIN{str="str std str2";gsub("str","STR",str);print str}'
awk 'BEGIN{str="this is a test";b=match(str,"is");print b}'
awk 'BEGIN{str="this is a test2016";print substr(str,4,6)}'
netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50
awk 'BEGIN{print mktime("2016 06 21 11 22 33")}'
awk 'BEGIN{print strftime("%Y-%m-%d %H:%M:%S",systime())}'
awk '{system("mkdir " ($NF-1)}' filename    # 倒数第二个字段做为目录名创建目录


格式总是有问题,更好的阅读体验欢迎[url=https://githubecho.github.io/2017/06/25/Linux_awk/]到访" target=_blank>, url}' /var/log/httpd/access_log [/code]

ARGC
:命令行中参数的个数,其awk命令也算一个参数

ARGV
:其是一个数组,保存的是命令行所给定的各参数

awk '{print NF}' /etc/fstab         # 每行字段数,这里是数量引用,不是对应的值引用
awk '{print $NF}' /etc/fstab        # 每行中的最后一个字段的值
awk '{print NR}' file1 file2        # 会每把所有文档进行总的编号,而不是单独对文件进行编号
awk '{print FNR}' file1 file2       # 会对每个文件的行数进行单独的编号显示
awk '{print FILENAME}' file1        # 显示当前文件名,但会每行显示一次
awk 'END{print FILENAME}' file1     # 显示当前文件名,但只会显示一次
awk 'END{print ARGC}' /etc/fstab    # 显示共有几个参数


内置函数

rand()
:随机数,大于等于0,小于1

gsub(str1, str2, str)
:在str中将str1全部替换为str2,支持配符查找

index(str1, str2 )
:在str1内找首次出现str2出现的位置,没有则返回0

split(str,array[,fieldsep[,seps]])
:将str以fieldsep为分隔符分隔,并将结果保存至array数组中,下标为从0开始

length([str])
:返回string字符串中字符的个数

substr(str, start [, length])
:截取字符串,从start开始,取length个,start从1开始计数

system(command)
:执行系统命令并将结果返回至awk

systime()
:系统当前时间戳

mktime("YYYY MM DD HH MM SS[ DST]")
:指定日期时间戳,注意加引号

strftime(format [,timestamp [,utc-flage]])
:生成指定格式时间,具体格式参考date命令

tolower(str)
:转为小写

toupper(str)
:转为大写

awk 'BEGIN{str="this is a test";split(str,A," ");print length(A);for(i in A){print i,A[i];}}'
awk 'BEGIN{str="str std str2";gsub("str","STR",str);print str}'
awk 'BEGIN{str="this is a test";b=match(str,"is");print b}'
awk 'BEGIN{str="this is a test2016";print substr(str,4,6)}'
netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50
awk 'BEGIN{print mktime("2016 06 21 11 22 33")}'
awk 'BEGIN{print strftime("%Y-%m-%d %H:%M:%S",systime())}'
awk '{system("mkdir " ($NF-1)}' filename    # 倒数第二个字段做为目录名创建目录


格式总是有问题,更好的阅读体验欢迎[url=https://githubecho.github.io/2017/06/25/Linux_awk/]到访


以上内容参考马哥笔记。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux