ITPub博客

首页 > Linux操作系统 > Linux操作系统 > Linux文件处理三剑客之awk

Linux文件处理三剑客之awk

原创 Linux操作系统 作者:joshliu 时间:2023-11-13 16:57:31 0 删除 编辑

背景

awk语言,出自三位创始人姓氏的首字母(Alfred Aho 、Peter Weinberger 和 Brian Kernighan)。在2011年针对发起者Alfred Aho的采访中,问起为何会创作了awk语言,Alfred的回答是在上世纪80年代初,为了追踪经费预算或者追踪学生的成绩,同时避免用C或者其它语言长篇的语句来实现,所以创造了awk语言,旨在用少量的代码行数就能实现如此的功能。同样,与后期Larry Wall创作了Perl语言一样,这些大师们出于某个实际应用,又不想写一堆代码,所以正是在这种程序员独有的“懒惰”与“完美主义”的双重人格下,极简而又优雅的语言,就被创造了出来。

awk是一种优良的文本处理工具,它不仅是 Linux 中也是任何环境中现有的最强大的数据处理引擎之一,提供了极其强大的功能:样式装入、流控制、数学运算符、进程控制语句等等。而且内置了许多变量和函数,可以便于使用者在用awk处理文本的时候,简洁而又方便得通过少量代码就能获取所需信息。


格式

    在awk中,格式会严重影响输出的正确性,通常而言,两种常见的格式如下:

awk '{pattern + action}'  input_file
awk 'pattern {action}' input_file

    比如说,如果文件中第一列匹配关键字abc,那就打印最后一列:

  •     第一种格式,awk '{if($1 ~ /abc/) print $NF}' input_file

  •     第二种格式,awk '$1 ~ /abc/ {print $NF}' input_file


参数

    通常情况下, awk命令常用参数有-F与-v

    -F 输入内容的分隔符(field)

    -v 某个可以传入的参数(var=val)

    比如输入内容分隔符是竖线(|),如果要打印每一行的列数,可以运行

awk -F '|' '{print NF}' input_file

    如果要通过-v打印出“列数是”的关键字,可以运行

awk -F '|' -v 'dp=列数是' '{print dp, NF}' input_file


特殊变量

    细心的读者在上述用例中,已经发现了NF这样的关键字,其实在awk中,除了NF,还有很多其它类似的特殊变量:

  • $0:代表整行

  • $1:代表分隔后第一列;同理$2代表第二列,以此类推

  • NF:number of fields,代表一行中列的个数,所以$NF表示最后一列

  • NR:number of rows,代表该行的行号

  • FS:field separator,代表输入文件的列分隔符

  • OFS:output field separator,代表输出内容的列分隔符

    所以,如果要打印某个文件的第2行,通过awk可以如下执行:

awk 'NR == 2 {print}' input_file #通过格式二书写

    如果要将input_file(分隔符是'|')的第一列替换成test:

awk -F '|' '{$1="test";print}' input_file

    细心的读者同样也会发现,输出内容的分隔符与原始文件不一致,所以如果要将上述输出内容依旧保持文件最开始的分隔符'|':

awk -F '|' '{$1="test";OFS=FS;print}' input_file


内置函数

    为了文字处理更加便捷,awk开发了一系列的内置函数,通过这些函数能极大简化代码的行数,本文选取常用函数进行介绍:

  • length,长度

  • gsub,替换

  • substr,截取

  • split,分隔

length

    例如,用'|'作为分隔符,将input_file文件每一行的长度以及第二列的长度打印出来:

awk -F '|' '{print length,length($2)}' input_file

    注:上述length如果代表的是一行长度,会包括分隔符'|'


gsub

    对gsub函数而言,有两种格式,分别是:

  • gsub("a","b"),将单行中所有的"a"替换为"b"

  • gsub("a","b",sth),将sth中所有的"a"替换为"b"

    比如,如果要将input_file中,所有的"i"字符,替换为"%",可以运行:

awk -F '|' '{gsub("s","%");OFS=FS;print}' input_file

    如果要将第二列的所有的"s"替换为"%",可以运行:

awk -F '|' '{gsub("s","%",$2);OFS=FS;print}' input_file



substr

    与gsub类似,substr也有两种格式

  • substr(sth,num),将sth从num开始截图到最后

  • substr(sth,num1,num2),将sth从num1开始往后截取num2位

    注:上述sth可以是某个字符串,也可以是某个变量或者$1/$2这样某一个列

    例如将以'|'作为分隔符的第一列,从第2位开始截取到最后:

awk -F '|' '{a=substr($1,2);print a}' input_file

    或者从第2位开始往后截取2位:

awk -F '|' '{a=substr($1,2,2);print a}' input_file


split

    split主要作用是将某一串字符串,根据某个关键字来分隔,并将分割完的结果保存到某个数组中,格式如下:

  • split(sth,数组名,"分隔符")

    表示将sth按照“分隔符”切分,并将切分后的结果保存到数组“数组名”中。

    比如将字符串"this|is|a|test"按照"|"切分,并打印第二部分内容

echo "this|is|a|test" | awk '{split($0,arr,"|");print arr[1]}'

    注:上述arr为人为定义的一个数组名称,并且数组的下标从1开始


运算与判断

    在任何一门编程语言中,离不开各式各样的运算以及判断,awk支持多种运算,这些运算与C语言提供的基本相同:如+、-、*、/、%等等,同时awk也支持C语言中类似++、--、+=、-=、=+、=-之类的功能,这给熟悉C语言的使用者编写awk程序带来了极大的方便。作为对运算功能的一种扩展,awk还提供了一系列内置的运算函数(如log、sqr、cos、sin等等)和一些用于对字符串进行操作(运算)的函数(如length、substr等等)。这些函数的引用大大的提高了awk的运算功能。

    在判断上,awk也提供了相当多功能,如常用的==(等于)、!=(不等于)、>(大于)、>=(大于等于)、<=(小于等于)等等,同时作为样式匹配,还提供了~(匹配于)和!~(不匹配于)判断。

    作为对测试的一种扩充,awk也支持用逻辑运算符:!(非)、&&;(与)、||(或)和括号()进行多重判断,这大大增强了awk的功能。


BEGIN/END

    BEGIN和END的作用是给程序赋予初始状态和在程序结束之后执行一些扫尾的工作。任何在BEGIN之后列出的操作(在{}内)将在awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。

    我们知道通过ps aux可以查询系统中进程相关信息,并且显示每个进程的CPU以及MEM使用:

    如果想要知道系统中所有进程使用的总CPU与总MEM之和,通过awk可以如下实现:

ps aux | awk 'BEGIN{cpusum=0;memsum=0} {cpusum=cpusum+$3;memsum=memsum+$4} END{print cpusum,memsum}'

流程控制

    awk提供了2种类型的流程控制,如下:

  •     条件判断

  •     循环

    

条件判断

    在awk中,条件判断一般为if .. else或者if .. else if .. else两种形式,使用方式与C语言相同。

    比如判断系统中每个进程CPU使用是否大于0,并打印对应信息,命令如下:

ps aux | awk 'NR>1 {if ($3>0) {print "进程"$2"CPU使用大于0"} else {print "进程"$2"CPU使用等于0"}}'

    

循环

    awk允许使用while、do..while和for语句来进行循环操作,同时使用break、continue语句来控制流程走向,也允许使用exit这样的语句来退出,使用方式与C语言类似。

    比如,分别通过while、do .. while以及for来打印0-9:

  •     awk 'BEGIN{i=0;while(i<10) {print i;i++}}'

  •     awk 'BEGIN{i=0;do{print i;i++}while(i<10)}'

  •     awk 'BEGIN{for (i=0;i<10;i++) {print i}}'

    如果上述i等于5,则退出循环:

awk 'BEGIN{for 
(i=0;i<10;i++) { if (i==5) {break};print i}}'


部分示例

日志筛选

    在对日志进行筛选的时候,比如说/var/log/messages这样的系统日志,如果我们想选取出11月12日时间在3点10分到3点14分之间的日志,可运行如下命令:

awk '$1=="Nov" && $2 == 12 && $3 >="03:10" && $3 <="03:14" {print}' /var/log/messages


文件中某列最大长度

    数据库从业者一定会碰到将某个数据文件加载到数据库这样的场景,有时候会碰到文件中某一列长度过长,从而导致无法加载进数据库(数据库端报错通常为char或者varchar定义长度不够),此时就需要查看该列最长的字符有多长,从而可以在数据库对应的表中设置合适的char(n)或者varchar(n)这样的n值,为了查看文件中该列最长的字符,可以运行:

awk 'BEGIN{max=0} {tmp=length($NF);if(tmp>max) {max=tmp}} END{print max}' input_file #本示例中以求最后一列$NF最大长度为例


文件内容替换

    在文件处理时,有时候需要将满足条件的行(比如说行号在10-100,或者第三列匹配abc选取出来的行),对这部分行的第二列内容,将所有的"test"字符替换为"test1"字符,命令如下:

awk '$3 ~ /abc/ {gsub("test","test1",$2);print}' input_file

总结

    awk是Linux上强大的文件处理工具,其主要作用是对所有行或者部分行,进行列上的各种操作,其中$0代表整行数据,$1代表第一列,$2代表第二列数据。通过-F参数可以指定输入内容的分隔符(同样支持多分隔符),并支持NF、NR、FS、OFS等特殊的变量,便于使用者获取文件内的各种信息(列数、行号、输入分隔符、输出分隔符)等。同时内置了很多函数,length/gsub/substr/split/sprintf等等,简化了文件处理的难度。为了处理文件时能有预置条件或者结束动作,awk提供了BEGIN与END的功能,同时在条件判断上支持与C语言相同的if..else用法。有时候为了实现更加复杂的功能,awk也支持循环,能让用户像C语言一样进行迭代计算或者遍历数组。

    总之,awk本身就是一种语言,可以毫不夸张的说,awk可以实现Linux上绝大部分的文本处理,尽管有时候不最完美的实现方式,不过对文件处理来说,awk已经足够了。




来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69985104/viewspace-2994860/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2020-10-09

  • 博文量
    78
  • 访问量
    74236