Linux之GAS: awk小记

1 说明

man awk:
awk - pattern scanning and processing language

awk的名字来源于三位创始人的姓氏:Alfred Aho,Peter Weinberger, 和 Brian Kernighan。类似sed,awk每次读取一行为进行处理,但处理的最小单位是字段(也就是列,通常使用空格或制表符分隔),因此适合处理表格一类的数据。但同时awk不止是一个程序/命令,还是一门程序设计(脚本式的)语言,常应用于各种计算和数据处理任务。

参考资料

  • mylxsw, 三十分钟学会AWK, 2016-10-31
  • AWK入门指南: 中文翻译,不完整, 2017-10-26
  • Jiavan, awk 入坑指北, 2018-01-10
  • vvpale, linux基础命令介绍八:文本分析 awk, 2016-11-23
  • wuzhouhui的github仓库, The AWK Programming Language
  • GNU awk用户手册

2 基本

awk命令的基本形式:

awk [options] SCRIPT [file]

SCRIPT结构:
脚本通常分为三部分,其中BEGIN,END部分是可选部分;当只有BEGIN部分是可以没有输入文件。其中:

  • BEGIN的action会在输入开始之前执行;
  • ENG的action会在所有行处理之后执行。

另外,awk作为编程语言,可以定义函数,可以包含注释(通常在文件中,以保持可读性和可重用性),使用#注释该字符所在的行(不包括之前的字符)。

BEGIN {action}
pattern {action}
END {action}

awk处理的基本流程:

  1. 按行读取(从文件或标准输入等),每一行称为一条记录record,使用$0表示;
    1.1 当文件缺省时,从标准输入中读取;使用Ctrl+D终止程序
  2. 对每一行执行SCRIPT,包括检查是否满足pattern,满足则执行action部分;其中:
    2.1 pattern省略是表示没有条件限制;
    2.2 action省略是表示执行{print}即打印当前记录,两者不可同时省略。

选项

常用的选项包括:

  • -f progfile; --file=progfile 从file中读取awk指令。
  • -F fs; --field-separator=fs 指定分隔符:
awk -F: '{print $1, $NF}' file.txt
# -F后的内容是正则表达式形式
awk -F '[.,:]' '{print $1, $NF}' file.txt
  • -v var=val; --assign=var=val 设定变量
awk -v n=5 'BEGIN{for(i=0;i
  • -d[=file]; --dump-variables[=file] 输出排序后的全局变量和值;默认文件输出文件是awkvars.out
  • -p[file]; --profile[=file] 格式化awk程序,默认输出的文件是awkprof.out
awk --profile 'BEGIN{printf"---|Header|--\n"} {print} 
END{printf"---|Footer|---\n"}' marks.txt > /dev/null 

其他选项

  • -V, --version 版本信息
  • --help 帮助信息

SCRIPT之使用pattern过滤

  • pattern部分可以由多个正则表达式或算式由逻辑运算符连接而成
  • pattern之间可以使用,分隔,表示两个匹配模式之间的记录

例子:

awk 'BEGIN{print "----"} {print $1} END{print "-----"}'
awk '/^root/,/^adm/' /etc/passwd   

SCRIPT之使用action执行

pattern之后的{}的内容表示 action;当actiono多个时可以使用;或换行进行分隔。

这里先简单介绍print:
print表示输出,期间可以使用$数组, 字符串需要使用引号包围。

  • 字段之间使用空格或,进行分隔
  • 可以重定向输出>>>保存输出
    此外还有格式化输出的printf
    另外awk中自定义变量,使用=进行赋值(直接使用,不需要向shell一样使用$标志),数组,条件语句等。详细的见【程序设计部分】。

例子:

# $5即文件大小字段,$NF即文件名字段
# && 逻辑与;`/../`表示正则表达式; ~表示匹配
ls -l *|awk '$5>20 && $NF ~ /txt$/'

# 列出第1,3列
last -n 5 | awk '{print $1 "\t" $3}'

# 设置分隔符是:, 第3列值小于10是打印第1列(账号)和第3列(UID)
cat /etc/passwd | awk '{FS=":"} $3 < 10 {print $1 "\t " $3}'
# 增加显示第一行(root);默认第一行是字段说明所以不直接处理
cat /etc/passwd | awk 'BEGIN{FS=":"} $3 < 10 {print $1 "\t " $3}'

# 复杂例子;从文件中读取awk
# test.awk中的内容
BEGIN{
    $1=1
    $2=1
    OFS=","
    for(i=3;i<=10;i++)
    {
        $i=$(i-2)+$(i-1)
    }
    print
}
# 打印斐波那契数列前10项
awk -f test.awk

# 定义变量
awk '{a=$1} {print a,$5}' file.txt
awk -v a="name" '{print a}'

# 使用数组
$ awk -F ':' '{a[$NF]++}END{for(i in a) print i,a[i]}' /etc/passwd

# 使用条件语句
netstat -antp | awk '{if($6=="LISTEN"){x++}else{y++}}END{print x,y}'

3 程序设计

内建变量

内建变量 说明
$0 当前处理记录(即当前读取的一行内容)
$1-$n 当前记录的第n个字段(列)
FS 字段/列分隔符,默认是空格或制表符
NF 当前记录的字段数目
NR 已经处理的记录条数,即当前行号
FNR 当前记录数,是每个文件的行号,NR是总数
RS 记录/行之间分隔符,默认是换行符\n
CONVFMT 数字转换格式 %.6g
OFS 输出字段分隔符,默认是空格
ORS 输出记录分隔符,默认是换行符\n
FIELDWIDTHS 输入字段宽度的空白分隔字符串
OFMT 数字的输出格式,%.6g
ARGC 命令行参数个数
ARGV 命令行参数数组
FILENAME 当前输入文件名字
ARGIND 当前被处理文件的ARGV标志符
ENVIRON UNIX/SHELL环境变量,数组形式
ERRNO UNIX系统错误消息
RSTART 被匹配函数匹配的字符串首
RLENGTH 匹配函数匹配的字符串长度
IGNRECASE 记录为真代表忽略大小写匹配
SUBSEP \0340x1C;文件分割符)
PROCINFO 当前运行程序进程信息数组

此外还有一些GNU awk扩展: 略

运算符

基本和C语言一致,基本的有:

  • 赋值:=
  • 算术运算符:+-*/%;单目 ++, --, -(相反数), ^(指数)
  • 关系运算符:>, >=, <, <=, ==, !=
  • 逻辑运算符:&&, ||, !; 常在条件、循环等语句中使用
  • 三目:(a>b)? max=a:max=b;
  • 字符串连接:str3 =str1 str2
  • 数组:[]成员操作符
    • 数字索引的数组(可以不连续),字符串索引的数组(类似字典);
      不需要预先声明
  • 正则:~!~分别表示匹配和不匹配: $0 ~ /[a-z]/
    • 正则表达式:需要使用斜杠/对表达式进行包围

例子:

awk 'BEGIN { a = 50; b = 20; print "(a + b) = ", (a + b) }'

控制语句

  • 条件语句: if-else
  • 循环语句:for, while
  • {}表示语句块,可以进一步嵌套

例子:来自

#  计算复利
#   输入: 钱数    利率    年数
#   输出: 复利值
{   i = 1
    while (i <= $3) {
        printf("\t%.2f\n", $1 * (1 + $2) ^ i)
        i = i + 1
    }
}

用户自定义函数

function function_name(argument1, argument2, ...) { 
   function body
}

例子:来自

# 脚本文件
# Returns minimum number
function find_min(num1, num2){
   if (num1 < num2)
   return num1
   return num2
}
# Returns maximum number
function find_max(num1, num2){
   if (num1 > num2)
   return num1
   return num2
}
# Main function
function main(num1, num2){
   # Find minimum number
   result = find_min(10, 20)
   print "Minimum =", result
  
   # Find maximum number
   result = find_max(10, 20)
   print "Maximum =", result
}
# Script execution starts here
BEGIN {
   main(10, 20)
}

调用外部shell程序/命令

  • 管道
  • system函数

例子:

# 管道方式
while (more stuff to do)
    print command | "/bin/sh"
close("/bin/sh")

# system函数
END { system("date | mail -s 'awk run done' root")}


4 内建函数

数学相关函数

  • exp, log
  • int, sqrt,
  • rand, srand
  • sin, cos, atan2

字符串相关

  • length() 获得字符串长度
awk -F: '{if(length($1)>=16) print}' /etc/passwd 
  • split()将字符串按分隔符分隔,并保存至数组
head -1 /etc/passwd|awk '{split($0,arr,/:/);for(i=1;i<=length(arr);i++) print arr[i]}'
  • sub(regex,substr,string) 替换string中匹配regex的第一项为substr;string为空则默认是$0;
echo 178278 world | awk 'sub(/[0-9]+/,"hello")'
  • gsub: 类似sub,全部替换
  • substr
  • tolower
  • match

时间

  • systime
  • mktime(datespec)
  • strftime([format [, timestamp[, utc-flag]]])

bit操作

  • and, or, xor
  • compl(val): Return the bitwise complement of val.
  • 移位 lshift, rshift

其他(IO等)

  • close(expr) 关闭管道文件
  • delete 删除数组元素
  • exit 退出脚本执行并返回状态码
  • getline 读取下一行内容
  • next
  • return
  • system(cmd) 执行外部shell的cmd命令,返回的结果0表示成功

你可能感兴趣的:(Linux之GAS: awk小记)