Linux

Awk 命令详解

2020-12-19  本文已影响0人  Whyn

[TOC]

Awk 脑图

简介

我们常在命令行中使用awk命令提取转换文件文本内容,可以说,awk是命令行中文本处理的瑞士军刀,其功能十分强大,几乎其他文本处理命令能做的,awk都可以做。

awk之所以能具备如此强大的文本处理能力,其原因在于awk不仅仅只是一个命令行程序,它本质上是一门图灵完备的动态类型的领域特定编程语言,专门用于操作文本内容。

可以这样认为,系统中存在的awk命令本质上是一个解释器,它可以对 AWK 编程语言源码进行解释执行。

本篇博文介绍下 AWK 语言相关内容,涵盖语言绝大多数特性。
熟读本篇博文,基本上日常生活遇到的与文本处理相关的操作,都能熟练使用awk进行处理。

安装

默认情况下,类 Unix 系统已内置awk命令,但版本可能比较旧,可以从 Awk 源码 下载最新版本。

当前的最新版本为 gawk-5.1.0,其源码安装方式如下所示:
gawk表示GNU awk

# 卸载 awk
$ sudo apt remove --purge gawk
# 下载最新版本 awk
$ sudo wget -c 'http://git.savannah.gnu.org/cgit/gawk.git/snapshot/gawk-5.1.0.tar.gz'
$ sudo tar xvf gawk-5.1.0.tar.gz
# 生成 Makefile
$ sudo ./configure
# 编译,本地生成 gawk
$ sudo make
# 安装
$ sudo make install

:安装完成后,需要重启下终端,让系统定位到新安装的 Awk,而不是卸载的 Awk。

最后可通过以下命令查看awk版本:

$ awk --version | head -n 1
GNU Awk 5.1.0, API: 3.0

执行模型

awk命令的格式如下所示:

awk [options] 'pattern { action }' [file]

:除了从文件中读取文本外,awk也支持直接从标准输入流中读取文本。

其中,pattern { action }该部分就是 AWK 语言进行编写的脚本源码,其结构如下:

pattern1 { action; }
pattern2 { action; }
...
patternn { action; }

即可以有一个或多个pattern { action }块,当进行文本处理时,首先会将第一行文本匹配pattern1,如果匹配成功,则执行pattern1action,然后继续将第一行文本匹配pattern2,如果不匹配,则跳过pattern2,继续匹配pattern3,依此类推,直至最后一个匹配完成,然后就可以进行第二行匹配,重复上述逻辑,直至全部文本匹配完成,这整个过程就是awk的执行模型。

对于pattern { action }块,其中:

patternaction任意一个可忽略不写,但不能同时进行忽略,其中:

命令行选项

awk是一个命令行工具,他本身支持一些选项options,这里主要介绍三个参数选项:

数据类型

AWK 语言提供了如下几种数据类型:

变量

AWK 是动态类型语言,因此其变量可直接定义,无需声明:

$ echo | awk '{ var = "hello world"; print(var) }'
hello world

:当引用未定义的变量时,其值为0""

AWK 还内置了一些变量,可以方便我们获取当前行字符串相关信息,具体内置变量如下表所示:

内置变量 含义
ARGC 命令行参数个数
ARGV 命令行参数数组
CONVFMT 数字转字符串的内部转换格式,默认为%.6g
ENVIRON 环境变量数组
FILENAME 当前操作的文件名
FNR 当前遍历的文件的行记录(即行号)
OFMT 数值类型打印格式,默认为%.6g
OFS 字段输出分隔符,默认为" "
ORS 每条输出记录的终止符,默认为\n
RLENGTH 上一次调用match()时设置的长度
RSTART 上一次调用match()时的索引
RS 输入记录(即行)的分隔符,默认为\n
SUBSEP 用以构建多维数组子脚本,默认值为\034
FS 自定义分隔符(支持正则表达)
NR 当前输入流的记录数量,对应当前遍历的行号
NF 当前行的字段数量
$0 当前行的内容
$1 当前行的第一个字段内容
$2 当前行的第二个字段内容
$n 当前行的第 n 个字段内容

FNR表示文件记录数量,每遍历一个新文件,该数值从0开始计起。而NR表示所有输入流的记录总数量,当输入多个文件时,会进行叠加,最终结果就是这些文件的所有记录数,即所有文件的总行数。

NR大意为number of rows,表示当前遍历的行号;NF大意为number of fields,表示当前行的字段数量。

下面是一个使用内置变量的大概结构:

BEGIN {       # 用户可以修改
  FS = ",";   # 内容分割符
  RS = "\n";  # 行(记录)分割符
  OFS = " ";  # 输出内容分割符
  ORS = "\n"; # 输出行(记录)分割符
}
{             # 用户无法修改
  NF          # 当前行字段(列)数量
  NR          # 当前行的行数
  ARGV / ARGC # 脚本参数
}

遇到具体问题时,套用上面的结构进行适当修改即可。

举个例子:模拟命令行工具wc -w,计算文件总字数:

echo -e 'line1 2 3\nline2 5' | awk 'BEGIN { count = 0 }   \
/[a-zA-Z0-9]+/ {                                          \
    printf("line%d: content = %s, words=%d\n", NR, $0, NF); \
    count += NF;                                          \
    }                                                     \
END { print("total words:",count) }'
line1: content = line1 2 3, words=3
line2: content = line2 5, words=2
total words: 5

运算符/操作符

AWK 语言支持以下运算符:

算术运算符(Arithmetic Operators)

AWK 语言支持的算法运算符如下表所示:

Operator Description
+ 加法运算
- 减法运算
* 乘法运算
/ 除法运算
% 取余
^ 幂运算

赋值运算符(Assignment Operators)

AWK 语言支持的赋值运算符如下表所示:

Operator Description
= 等于(赋值)
+= 加等
-= 减等
*= 乘等
/= 除等
%= 余等
^= 幂等

关系/比较运算符(Relational (Comparison) Operators)

AWK 语言支持的关系/比较运算符如下表所示:

Operator Description
< 小于
> 大于
<= 小于或等于
>= 大于或等于
== 等于
!= 不等于

:关系运算中,==对不同类型的变量进行比较,对隐式自动进行类型转换,这与 JavaScript 语言的==操作符效果一样:

$ echo | awk '{ a = 10; b = "10"; print( a==b ? "true" : "false" ) }'
true

逻辑运算符(Logical Operators)

AWK 语言支持的逻辑运算符如下表所示:

Operator Description
|| 与运算
&& 或运算
! 非运算

一元运算符(Unary Operators)

如下表所示:

Operator Description
+ 正号
- 符号(取反)
++ 自增
-- 自减
<> <>

:AWK 语言的自增、自减操作均支持前置/后置运算。

三目运算符

三目运算符操作如下所示:

$ echo | awk '{ ret = 10 > 2 ? "true" : "false"; print(ret) }'
true

流程控制

AWK 语言流程控制语句主要有如下三类:

条件判断

条件判断语句使用if关键字,其格式如下例子所示:

# 格式一:if()
$ awk 'BEGIN {                             \
    if ( 10 > 1 ) { # 单条语句则括号可省略
        print ("true")
    }
}'
true

# 格式二:if else
$ awk 'BEGIN {                               \
    if ( 1 > 10 ) {
        print("false")
    } else {
        print ("true")
    }
}'
true

循环控制语句

AWK 语言主要提供两种类型循环控制语句:

中断控制语句

AWK 语言主要提供的中断操作有:

举个例子:

seq 1 5 | awk -v seed=$RANDOM 'BEGIN { srand(see) }
    {
        for ( i = 0; i < 10; ++i ) {
            # random number
            num = sprintf("%d",rand() * 10)
            print("num=",num)
            if ( num % 2 ) {
                # skip odd
                continue
            }
            if (num >= 8) {
                print("break-------")
                break
            }
            if (num == 6 ) {
                print("binggo!!!!!!!!")
                exit(0)
            }
        }
    }'

函数

AWK 语言支持函数定义与调用,如下例子所示:

这里创建一个文件test.awk,编写如下代码:

# 函数定义
function myfunc(n) {
    for (i = 1; i <= n; ++i){
        print $i;
    }
}

# 主体执行块
{
    # 调用函数
    myfunc(NF)
}

命令行允许该源码文件:

$ echo -e 'field1 field2' | awk -f test.awk
field1
field2

其实如果需要使用到自定义函数,那直接使用 Python 等脚本语言其实更方便,一般使用awk命令都不会使用到自定义函数,但是 AWK 语言也提供了一些内置函数,可以方便我们使用,这确实非常不错的。

AWK 提供的内置函数有很多,具体内容可查看:Built-in Functions

以下就简单介绍几个本人较常用的内置函数:

输入输出

字符串函数

数值操作函数

参考

上一篇 下一篇

猜你喜欢

热点阅读