AWK教程

来源:互联网 发布:淘宝威龙爱疯能信吗 编辑:程序博客网 时间:2024/04/27 16:28

一. AWK入门指南

Awk是一种便于使用且表达能力强的程序设计语言,可应用于各种计算和数据处理任务。本章是个入门指南,让你能够尽快地开始编写你自己的程序。第二章将描述整个语言,而剩下的章节将向你展示如何使用Awk来解决许多不同方面的问题。纵观全书,我们尽量选择了一些对你有用、有趣并且有指导意义的实例。

1.1 起步

有用的awk程序往往很简短,仅仅一两行。假设你有一个名为 emp.data 的文件,其中包含员工的姓名、薪资(美元/小时)以及小时数,一个员工一行数据,如下所示:

Beth4.000Dan3.750kathy4.0010Mark5.0020Mary5.5022Susie4.2518

现在你想打印出工作时间超过零小时的员工的姓名和工资(薪资乘以时间)。这种任务对于awk来说就是小菜一碟。输入这个命令行就可以了::

awk '$3 >0 { print $1, $2 * $3 }' emp.data 

你应该会得到如下输出:

Kathy 40
Mark 100
Mary 121
Susie 76.5

该命令行告诉系统执行引号内的awk程序,从输入文件 emp.data 获取程序所需的数据。引号内的部分是个完整的awk程序,包含单个模式-动作语句。模式 $3>0用于匹配第三列大于0的输入行,动作:

{ print $1, $2 * $3 } 

打印每个匹配行的第一个字段以及第二第三字段的乘积。

如果你想打印出还没工作过的员工的姓名,则输入命令行::

awk '$3 == 0 { print $1 }' emp.data 

这里,模式 $3 == 0 匹配第三个字段等于0的行,动作:

{ print $1 } 

打印该行的第一个字段。

当你阅读本书时,应该尝试执行与修改示例程序。大多数程序都很简短,所以你能快速理解awk是如何工作的。在Unix系统上,以上两个事务在终端里看起来是这样的:

$ awk ‘$3 > 0 { print $1, $2 * $3 }’ emp.data
Kathy 40
Mark 100
Mary 121
Susie 76.5
$ awk ‘$3 == 0 { print $1 }’ emp.data
Beth
Dan
$

行首的 $ 是系统提示符,也许在你的机器上不一样。

AWK程序的结构

让我们回头看一下到底发生了什么事情。上述的命令行中,引号之间的部分是awk编程语言写就的程序。本章中的每个awk程序都是一个或多个模式-动作语句的序列:

pattern { action }
pattern { action }
...

awk的基本操作是一行一行地扫描输入,搜索匹配任意程序中模式的行。词语“匹配”的准确意义是视具体的模式而言,对于模式 $3 >0 来说,意思是“条件为真”。

每个模式依次测试每个输入行。对于匹配到行的模式,其对应的动作(也许包含多步)得到执行,然后读取下一行并继续匹配,直到所有的输入读取完毕。

上面的程序都是模式与动作的典型示例。:

$3 == 0 { print $1 } 

是单个模式-动作语句;对于第三个字段为0的每行,打印其第一个字段。

模式-动作语句中的模式或动作(但不是同时两者)都可以省略。如果某个模式没有动作,例如::

$3 == 0 

那么模式匹配到的每一行(即,对于该行,条件为真)都会被打印出来。该程序会打印 emp.data 文件中第三个字段为0的两行

Beth 4.00 0
Dan 3.75 0

如果有个没有模式的动作,例如::

{ print $1 } 

那么这种情况下的动作会打印每个输入行的第一列。

由于模式和动作两者任一都是可选的,所以需要使用大括号包围动作以区分于其他模式。

执行AWK程序

执行awk程序的方式有多种。你可以输入如下形式的命令行::

awk 'program' input files 

从而在每个指定的输入文件上执行这个program。例如,你可以输入::

awk '$3 == 0 { print $1 }' file1 file2 

打印file1和file2文件中第三个字段为0的每一行的第一个字段。

你可以省略命令行中的输入文件,仅输入::

awk 'program' 

这种情况下,awk会将program应用于你在终端中接着输入的任意数据行,直到你输入一个文件结束信号(Unix系统上为control-d)。如下是Unix系统的一个会话示例:

$ awk ‘$3 == 0 { print $1 }’
Beth 4.00 0

Beth

Dan 3.75 0

Dan

Kathy 3.75 10
Kathy 3.75 0

Kathy

...

加粗的字符是计算机打印的。

这个动作非常便于尝试awk:输入你的程序,然后输入数据,观察发生了什么。我们再次鼓励你尝试这些示例并进行改动。

注意命令行中的程序是用单引号包围着的。这会防止shell解释程序中 $ 这样的字符,也允许程序的长度超过一行。

当程序比较短小(几行的长度)的时候,这种约定会很方便。然而,如果程序较长,将程序写到一个单独的文件中会更加方便。假设存在程序 progfile ,输入命令行::

awk -f progfile optional list of input files 

其中 -f 选项指示awk从指定文件中获取程序。可以使用任意文件名替换 progfile 。

错误

如果你的awk程序存在错误,awk会给你一段诊断信息。例如,如果你打错了大括号,如下所示::

awk '$3 == 0 [ print $1 }' emp.data 

你会得到如下信息:

awk: syntax error at source line 1
context is
$3 == 0 >>> [ <<<
extra }
missing ]
awk: bailing out at source line 1

“Syntax error”意味着在 >>> <<< 标记的地方检测到语法错误。“Bailing out”意味着没有试图恢复。有时你会得到更多的帮助-关于错误是什么,比如大括号或括弧不匹配。

因为存在句法错误,awk就不会尝试执行这个程序。然而,有些错误,直到你的程序被执行才会检测出来。例如,如果你试图用零去除某个数,awk会在这个除法的地方停止处理并报告输入行的行号以及在程序中的行号(这话是什么意思?难道输入行的行号是忽略空行后的行号?)。

1.2 简单输出

这一节接下来的部分包含了一些短小,典型的awk程序,基于操纵上文中提到的 emp.data 文件. 我们会简单的解释程序在做什么,但这些例子主要是为了介绍 awk 中常见的一些简单有用的操作 – 打印字段, 选择输入, 转换数据. 我们并 没有展现 awk 程序能做的所有事情, 也并不打算深入的去探讨例子中的一些细节. 但在你读完这一节之后, 你将能够完成一些简单的任务, 并且你将发现在阅读后 面章节的时候会变的容易的多.

我们通常只会列出程序部分, 而不是整个命令行. 在任何情况下, 程序都可以用 引号包含起来放到 awk 命令的地一个参数中运行, 就像上文中展示的那样, 或者 把它放到一个文件中使用 awk 的 -f 参数调用它.

在 awk 中仅仅只有两种数据类型: 数值 和 字符构成的字符串. emp.data 是 一个包含这类信息的典型文件 – 混合了被空格和(或)制表符分割的数字和词语.

Awk 程序一次从输入文件的中读取一行内容并把它分割成一个个字段, 通常默认 情况下, 一个字段是一个不包含任何空格或制表符的连续字符序列. 当前输入的 行中的地一个字段被称做 $1, 第二个是 $2, 以此类推. 整个行的内容被定 义为 $0. 每一行的字段数量可以不同.

通常, 我们要做的仅仅只是打印出每一行中的某些字段, 也许还要做一些计算. 这一节的程序基本上都是这种形式.

打印每一行

如果一个动作没有任何模式, 这个动作会对所有输入的行进行操作. print 语 句用来打印(输出)当前输入的行, 所以程序

{ print } 

会输出所有输入的内容到标准输出. 由于 $0 表示整行,

{ print $0 } 

也会做一样的事情.

打印特定字段

使用一个 print 语句可以在同一行中输出不止一个字段. 下面的程序输出了每 行输入中的第一和第三个字段

{ print $1, $3 } 

使用 emp.data 作为输入, 它将会得到

Beth 0 Dan 0 Kathy 10 Mark 20 Mary 22 Susie 18 

在 print 语句中被逗号分割的表达式, 在默认情况下他们将会用一个空格分割 来输出. 每一行 print 生成的内容都会以一个换行符作为结束. 但这些默认行 为都可以自定义; 我们将在第二章中介绍具体的方法.

NF, 字段数量

很显然你可能会发现你总是需要通过 $1, $2 这样来指定不同的字段, 但任何表 达式都可以使用在$之后来表达一个字段的序号; 表达式会被求值并用于表示字段 序号. Awk会对当前输入的行有多少个字段进行计数, 并且将当前行的字段数量存 储在一个内建的称作 NF 的变量中. 因此, 下面的程序

{ print NF, $1, $NF } 

会依次打印出每一行的字段数量, 第一个字段的值, 最后一个字段的值.

计算和打印

你也可以对字段的值进行计算后再打印出来. 下面的程序

{ print $1, $2 * $3 } 

是一个典型的例子. 它会打印出姓名和员工的合计支出(以小时计算):

Beth 0 Dan 0 Kathy 40 Mark 100 Mary 121 Susie 76.5 

我们马上就会学到怎么让这个输出看起来更漂亮.

打印行号

Awk提供了另一个内建变量, 叫做 NR, 它会存储当前已经读取了多少行的计数. 我们可以使用 NR 和 $0 给 emp.data 的没一行加上行号:

{ print NR, $0 } 

打印的输出看起来会是这样:

1 Beth 4.00 0 2 Dan 3.75 0 3 Kathy 4.00 10 4 Mark 5.00 20 5 Mary 5.50 22 6 Susie 4.25 1 8 

在输出中添加内容

你当然也可以在字段中间或者计算的值中间打印输出想要的内容:

{ print "total pay for", $1, "is", $2 * $3 } 

输出

total pay for Beth is 0 total pay for Dan is 0 total pay for Kathy is 40 total pay for Mark is 100 total pay for Mary is 121 total pay for Susie is 76.5 

在打印语句中, 双引号内的文字将会在字段和计算的值中插入输出.

1.3 高级输出

print 语句可用于快速而简单的输出。若要严格按照你所想的格式化输出,则需要使用 printf 语句。正如我将在2.4节所见, printf 几乎可以产生任何形式的输出,但在本节中,我们仅展示其部分功能。

字段排队

printf 语句的形式如下::

printf(format, value1, value2, ..., valuen) 

其中 format 是字符串,包含要逐字打印的文本,穿插着 format 之后的每个值该如何打印的规格(specification)。一个规格是一个 % 符,后面跟着一些字符,用来控制一个 value 的格式。第一个规格说明如何打印 value1 ,第二个说明如何打印 value2 ,... 。因此,有多少 value 要打印,在 format 中就要有多少个 %规格。

这里有个程序使用 printf 打印每位员工的总薪酬::

{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) } 

printf 语句中的规格字符串包含两个 % 规格。第一个是 %s ,说明以字符串的方式打印第一个值 $1 。第二个是 %.2f ,说明以数字的方式打印第二个值 $2*$3 ,并保留小数点后面两位。规格字符串中其他东西,包括美元符号,仅逐字打印。字符串尾部的 \n 代表开始新的一行,使得后续输出将从下一行开始。以 emp.data为输入,该程序产生:

total pay for Beth is $0.00 total pay for Dan is $0.00 total pay for Kathy is $40.00 total pay for Mark is $100.00 total pay for Mary is $121.00 total pay for Susie is $76.50 

printf 不会自动产生空格或者新的行,必须是你自己来创建,所以不要忘了 \n 。

另一个程序是打印每位员工的姓名与薪酬::

{ printf("%-8s $%6.2f\n", $1, $2 * $3) } 

第一个规格 %-8s 将一个姓名以字符串形式在8个字符宽度的字段中左对齐输出。第二个规格 %6.2f 将薪酬以数字的形式,保留小数点后两位,在6个字符宽度的字段中输出。

Beth $ 0.00 Dan $ 0.00 Kathy $ 40.00 Mark $100.00 Mary $121.00 Susie $ 76.50 

之后我们将展示更多的 printf 示例。一切精彩尽在2.4小节。

排序输出

假设你想打印每位员工的所有数据,包括他或她的薪酬,并以薪酬递增的方式进行排序输出。最简单的方式是使用awk将每位员工的总薪酬置于其记录之前,然后利用一个排序程序来处理awk的输出。Unix上,命令行如下:

awk '{ printf("%6.2f    %s\n", $2 * $3, $0) }' emp.data | sort 

将awk的输出通过管道传给 sort 命令,输出为:

  0.00 Beth 4.00 0 0.00 Dan 3.75 0 40.00 Kathy 4.00 10 76.50 Susie 4.25 18 100.00 Mark 5.00 20 121.00 Mary 5.50 22 

1.4 选择

Awk的模式适合用于为进一步的处理从输入中选择相关的数据行。由于不带动作的模式会打印所有匹配模式的行,所以很多awk程序仅包含一个模式。本节将给出一些有用的模式示例。

通过对比选择

这个程序使用一个对比模式来选择每小时赚5美元或更多的员工记录,也就是,第二个字段大于等于5的行::

$2 >= 5 

从 emp.data 中选出这些行::

Mark 5.00 20 Mary 5.50 22 

通过计算选择

程序

$2 * $3 > 50 { printf("$%.2f for %s\n", $2 * $3, $1) } 

打印出总薪资超过50美元的员工的薪酬。

通过文本内容选择

除了数值测试,你还可以选择包含特定单词或短语的输入行。这个程序会打印所有第一个字段为 Susie 的行::

$1 == "Susie" 

操作符 == 用于测试相等性。你也可以使用称为 正则表达式 的模式查找包含任意字母组合,单词或短语的文本。这个程序打印任意位置包含 Susie 的行::

/Susie/ 

输出为这一行::

Susie 4.25 18 

正则表达式可用于指定复杂的多的模式;2.1节将会有全面的论述。

模式组合

可以使用括号和逻辑操作符与 && , 或 || , 以及非 ! 对模式进行组合。程序:

$2 >= 4 || $3 >= 20 

会打印 $2 (第二个字段) 大于等于 4 或者 $3 (第三个字段) 大于等于 20 的行::

Beth 4.00 0 kathy 4.00 10 Mark 5.00 20 Mary 5.50 22 Susie 4.25 18 

两个条件都满足的行仅打印一次。与如下包含两个模式程序相比::

$2 >= 4 $3 >= 20 

如果某个输入行两个条件都满足,这个程序会打印它两遍::

Beth 4.00 0 Kathy 4.00 10 Mark 5.00 20 Mark 5.00 20 Mary 5.50 22 Mary 5.50 22 Susie 4.25 18 

注意如下程序:

!($2 < 4 && $3 < 20) 

会打印极不满足 $2 小于4也不满足 $3 小于20的行;这个条件与上面第一个模式组合等价,虽然也许可读性差了点。

数据验证

实际的数据中总是会存在错误的。在数据验证-检查数据的值是否合理以及格式是否正确-方面,Awk是个优秀的工具。

数据验证本质上是否定的:不是打印具备期望属性的行,而是打印可疑的行。如下程序使用对比模式 将5个数据合理性测试应用于 emp.data 的每一行::

NF != 3 { print $0, "number of fields is not equal to 3" } $2 < 3.35 { print $0, "rate is below minimum wage" } $2 > 10 { print $0, "rate exceeds $10 per hour" } $3 < 0 { print $0, "negative hours worked" } $3 > 60 { print $0, "too many hours worked" } 

如果没有错误,则没有输出。

BEGIN与END

特殊模式 BEGIN 用于匹配第一个输入文件的第一行之前的位置, END 则用于匹配处理过的最后一个文件的最后一行之后的位置。这个程序使用 BEGIN 来输出一个标题::

BEGIN { print "Name    RATE    HOURS"; print ""} { print } 

输出为::

NAME RATE HOURS Beth 4.00 0 Dan 3.75 0 Kathy 4.00 10 Mark 5.00 20 Mary 5.50 22 Susie 4.25 18 

程序的动作部分你可以在一行上放多个语句,不过要使用分号进行分隔。注意 普通的 print 是打印当前输入行,与之不同的是 print “” 会打印一个空行。

1.5 使用AWK进行计算

一个动作就是一个以新行或者分号分隔的语句序列。你已经见过一些其动作仅是单个 print 语句的例子。本节将提供一些执行简单的数值以及字符串计算的语句示例。在这些语句中,你不仅可以使用像 NF 这样的内置变量,还可以创建自己的变量用于计算、存储数据诸如此类的操作。awk中,用户创建的变量不需要声明。

计数

这个程序使用一个变量 emp 来统计工作超过15个小时的员工的数目::

$3 > 15 { emp = emp + 1 } END { print emp, "employees worked more than 15 hours" } 

对于第三个字段超过15的每行, emp 的前一个值加1。以 emp.data 为输入,该程序产生::

3 employees worked more than 15 hours 

用作数字的awk变量的默认初始值为0,所以我们不需要初始化 emp 。

求和与平均值

为计算员工的数目,我们可以使用内置变量 NR ,它保存着到目前位置读取的行数;在所有输入的结尾它的值就是所读的所有行数。

END { print NR, "employees" } 

输出为::

6 employees 

如下是一个使用 NR 来计算薪酬均值的程序::

    { pay = pay + $2 * $3 } END { print NR, "employees" print "total pay is", pay print "average pay is", pay/NR } 

第一个动作累计所有员工的总薪酬。 END 动作打印出

6 employees total pay is 337.5 average pay is 56.25 

很明显, printf 可用来产生更简洁的输出。并且该程序也有个潜在的错误:在某种不太可能发生的情况下, NR 等于0,那么程序会试图执行零除,从而产生错误信息。

处理文本

awk的优势之一是能像大多数语言处理数字一样方便地处理字符串。awk变量可以保存数字也可以保存字符串。这个程序会找出时薪最高的员工::

$2 > maxrate { maxrate = $2; maxemp = $1 } END { print "highest hourly rate:", maxrate, "for", maxemp } 

输出

highest hourly rate: 5.50 for Mary

这个程序中,变量 maxrate 保存着一个数值,而变量 maxemp 则是保存着一个字符串。(如果有几个员工都有着相同的最大时薪,该程序则只找出第一个。)

字符串连接

可以合并老字符串来创建新字符串。这种操作称为 连接(concatenation) 。程序

    { names = names $1 " "} END { print names } 

通过将每个姓名和一个空格附加到变量 names 的前一个值, 来将所有员工的姓名收集进单个字符串中。最后 END 动作打印出 names 的值::

Beth Dan Kathy Mark Mary Susie 

awk程序中,连接操作的表现形式是将字符串值一个接一个地写出来。对于每个输入行,程序的第一个语句先连接三个字符串: names 的前一个值、当前行的第一个字段以及一个空格,然后将得到的字符串赋值给 names 。因此,读取所有的输入行之后, names 就是个字符串,包含所有员工的姓名,每个姓名后面跟着一个空格。用于保存字符串的变量的默认初始值是空字符串(也就是说该字符串包含零个字符),因此这个程序中的 names 不需要显式初始化。

打印最后一个输入行

虽然在 END 动作中 NR 还保留着它的值,但 $0 没有。程序

    { last = $0 } END { print last } 

是打印最后一个输入行的一种方式::

Susie 4.25 18 

内置函数

我们已看到awk提供了内置变量来保存某些频繁使用的数量,比如:字段的数量和输入行的数量。类似地,也有内置函数用来计算其他有用的数值。除了平方根、对数、随机数诸如此类的算术函数,也有操作文本的函数。其中之一是 length ,计算一个字符串中的字符数量。例如,这个程序会计算每个人的姓名的长度::

{ print $1, length($1) } 

结果::

Beth 4 Dan 3 Kathy 5 Mark 4 Mary 4 Susie 5 

行、单词以及字符的计数

这个程序使用了 length 、 NF 、以及 NR 来统计输入中行、单词以及字符的数量。为了简便,我们将每个字段看作一个单词。

    { nc = nc + length($0) + 1 nw = nw + NF } END { print NR, "lines,", nw, "words,", nc, "characters" } 

文件 emp.data 有:

6 lines, 18 words, 77 characters 

$0 并不包含每个输入行的末尾的换行符,所以我们要另外加个1。

1.6 控制语句

Awk为选择提供了一个 if-else 语句,以及为循环提供了几个语句,所以都效仿C语言中对应的控制语句。它们仅可以在动作中使用。

if-else语句

如下程序将计算时薪超过6美元的员工的总薪酬与平均薪酬。它使用一个 if 来防范计算平均薪酬时的零除问题。

$2 > 6 { n = n + 1; pay = pay + $2 * $3 } END { if (n > 0) print n, "employees, total pay is", pay, "average pay is", pay/n else print "no employees are paid more than $6/hour" } 

emp.data 的输出是::

no employees are paid more than $6/hour 

if-else 语句中,if 后的条件会被计算。如果为真,执行第一个 print 语句。否则,执行第二个 print 语句。注意我们可以使用一个逗号将一个长语句截断为多行来书写。

while语句

一个 while 语句有一个条件和一个执行体。条件为真时执行体中的语句会被重复执行。这个程序使用公式 value=amount(1+rate)years

来演示以特定的利率投资一定量的钱,其数值是如何随着年数增长的。

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

条件是 while 后括弧包围的表达式;循环体是条件后大括号包围的两个表达式。 printf 规格字符串中的 \t 代表制表符; ^ 是指数操作符。从 # 开始到行尾的文本是注释,会被awk忽略,但能帮助程序的读者理解程序做的事情。

你可以为这程序输入三个一组的数字,看看不一样的钱数、利率、以及年数会产生什么。例如,如下事务演示了1000美元,利率为6%与12%,5年的复利分别是如何增长的::

$ awk -f interest1 1000 .06 5 1060.00 1123.60 1191.02 1262.48 1338.23 1000 .12 5 1120.00 1254.40 1404.93 1573.52 1762.34 

for语句

另一个语句, for ,将大多数循环都包含的初始化、测试、以及自增压缩成一行。如下是之前利息计算的 for 版本::

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

初始化 i = 1 只执行一次。接下来,测试条件 i <= $3 ;如果为真,则执行循环体的 printf 语句。循环体执行结束后执行自增 i = i + 1 ,接着由另一次条件测试开始下一个循环迭代。代码更加紧凑,并且由于循环体仅是一条语句,所以不需要大括号来包围它。

1.7 数组

awk为存储一组相关的值提供了数组。虽然数组给予了awk很强的能力,但在这里我们仅展示一个简单的例子。如下程序将按行逆序打印输入。第一个动作将输入行存为数组 line 的连续元素;即第一行放在 line[1] ,第二行放在 line[2] , 依次继续。 END 动作使用一个 while 语句从后往前打印数组中的输入行::

# 反转 - 按行逆序打印输入 { line[NR] = $0 } # 记下每个输入行 END { i = NR # 逆序打印 while (i > 0) { print line[i] i = i - 1 } } 

以 emp.data 为输入,输出为

Susie 4.25 18 Mary 5.50 22 Mark 5.00 20 Kathy 4.00 10 Dan 3.75 0 Beth 4.00 0 

如下是使用 for 语句实现的相同示例::

# 反转 - 按行逆序打印输入 { line[NR] = $0 } # 记下每个输入行 END { for (i = NR; i > 0; i = i - 1) print line[i] }
 

二. AWK语言详解

本章将主要通过示例来解释构成awk程序的概念。因为这是对语言的全面描述,材料会很详细,因此我们推荐你浏览略读,需要的时候再回来核对细节。

最简单的awk程序就是一个模式-动作语句的序列::

pattern { action } pattern { action } ... 

某些语句中,可能没有模式;另一些语句中,可能没有动作及其大括号。awk检查你的程序以确认不存在语法错误后,一次读取一行输入,并对每一行按序处理模式。对于每个匹配到当前输入行的模式,执行其关联的动作。不存在模式,则匹配每个输入行,因此没有模式的每个动作对于每个输入行都要执行。一个仅包含模式的模式-动作语句将打印匹配该模式的每个输入行。本章的大部分内容中,名词”输入行(input-line)”和”记录(record)” 是同义的。2.5小节中,我们将讨论多行记录,即一个记录包含多行输入。

本章的第一节将详细描述模式。第二节通过表达式、赋值以及控制语句来描述动作。剩下的章节覆盖函数定义,输出,输入,以及awk程序如何调用其他程序等内容。多数章节包含了主要特性的概要。

输入文件 countries

本章中,我们使用一个名为 countries 的文件作为许多awk程序的输入。文件的每行包含一个国家的名字,以千平方英里为单位的面积,以百万为单位的人口数,以及属于哪个洲。数据是1984年的,苏联(USSR)被武断地归入了亚洲。文件中,四列数据以制表符tab分隔;以单个空格将 North 、 South 与 America 分隔开。

文件 countries 包含如下数据行::

USSR 8649 275 Asia Canada 3852 25 North America China 3705 1032 Asia USA 3615 237 North America Brazil 3286 134 South America India 1267 746 Asia Mexico 762 78 North America France 211 55 Europe Japan 144 120 Asia Germany 96 61 Europe England 94 56 Europe 

本章的其余部分,如果没有明确说明输入文件,那么就是使用文件 countries 。

程序的格式

模式-动作语句以及动作中的语句通常以换行分隔,如果它们以分号分隔,则多个语句可以出现在一行中。分号可以放在任意语句的尾部。

动作的开大括号必须与其对应的模式处于同一行;动作的其余部分,包括闭大括号,则可以出现接下来的行中。

空行会被忽略;一般为了提高程序的可读性会在语句的前面或者后面插入空行。在操作符和操作数的两边插入空格和制表符也是为了提高可读性。

任意行的末尾可能会有注释。注释以符号 # 开始,结束于行尾,就像这样

{ print $1, $3 } # print country name and population 

长语句可以跨越多行,但要在断行的地方加入一个反斜杠和一个换行符::

{ print \                $1,             # country name                $2,             # area in thousands of square miles                $3 }    # population in millions

如上例所示,语句也可以逗号断行,在每个断行的末尾也可以加入注释。

本书中,我们使用了多种格式风格,部分是为了说明相异之处,部分是为了避免程序占用太多的行空间。类似于本章中的简短程序,格式并不是很重要,但一致性与可读性可以帮助更长的程序保持可控。

2.1 模式

模式控制着动作的执行:模式匹配,其关联的动作则执行。本节将描述6种模式及其匹配条件。


模式摘要

1. BEGIN { 语句 }
在读取任何输入前执行一次 语句
2. END { 语句 }
读取所有输入之后执行一次 语句
3. 表达式 { 语句 }
对于 表达式 为真(即,非零或非空)的行,执行 语句
4. /正则表达式/ { 语句 }
如果输入行包含字符串与 正则表达式 相匹配,则执行 语句
5. 组合模式 { 语句 }
一个 组合模式 通过与(&&),或(||),非(|),以及括弧来组合多个表达式;对于组合模式为真的每个输入行,执行 语句
6. 模式1,模式2 { 语句 }
范围模式(range pattern)匹配从与 模式1 相匹配的行到与 模式2 相匹配的行(包含该行)之间的所有行,对于这些输入行,执行 语句 。

BEGIN和END不与其他模式组合。范围模式不可以是任何其他模式的一部分。BEGIN和END是仅有的必须搭配动作的模式。




awk '{print}' /etc/passwd              :打印/etc/passwd所有内容

awk -F : '{print $1}' /etc/passwd                 :以 ":" 为分隔符 ,打印/etc/passwd第一字段,也就是用户名

awk -F : '{print $1 " " $2}' /etc/passwd                 :同上功能,打出第1,第二字段。  中间加个空格输出。

awk 'BEGIN{FS=":";OFS="\t";ORS="\n"}{print $1,$2}' /etc/passwd ***  FS:字段分隔符

    OFS:输出字段分隔符

    ORS:输出记录分隔符

以上命令为以 : 为分隔符,每个字段用\t隔开,输出一行后\n (换行), 输出第1,2字段。中间用,隔开

awk '{print NR}' /etc/passwd                 :读取/etc/passwd的每一行行号  (NR至今的读取记录数)

awk 'END{print NR}' /etc/passwd                  : 加了个END,读最后一个读取记录数的数字! 输出结果:29 表示29个用户

awk '/root/' /etc/passwd                   :输出 /etc/passwd里包含  root 这个字眼的 记录!打印出来 。'/ /'里表示的是符合字符

awk '{if($1~/root/) print $0}' /etc/passwd             : 如果第一个字段里有符合root字眼的,打印当前行,$0表示当前行(当前记录) awk '/^[Rr]oot/' /etc/passwd               :   '//'里表示匹配字符  ^表示匹配第一个字母 [Rr]表示这个字母是R或者r也可以

                                    所以上面的语句表示:第一个字母符合Root或者root就打印改记录条

awk '$2~/^..u/' /etc/passwd             : ~/表示匹配,这里表示第2个字段第3个字母为u的话,就打印出该行

awk '/(lin|root)/' /etc/passwd         :记录里有匹配 lin或者root的!就打印出来

echo ababab | awk 'gsub(/a/,"c")'           : 输出  ababab  再把输出结果交给awk处理,最后吧 a换成了c .输出为 cbcbcb

awk '{if($4!~"301|302") print $0}' filename           :要是 第4个字段, 不匹配  301或者 302!就打印出该记录  !~表示不匹配,~表示匹配

awk '$1!~/^.-/'  不匹配第2个字符是 -

cat XXX.txt |grep -v "^#"|grep -v "^$" > XXX.txt  把开头是#的!全部删掉

cat t.log |awk '{if($3!~/note/) print $0}'|grep "2010-11-14"|wc -l  不匹配note

cat t.log |awk '{if($3~/note/) print $0}'|grep "2010-11-14"|wc -l  匹配note

cat t.log |grep -E "2010-11-14|2010-11-15" |more

cat t.log |grep -E "2010-11-14|2010-11-15" |awk '{if($3~/test/) print $0}' |more

cat /etc/passwd |cut -d ":" -f1 |grep root

cat tmp |awk '{if($3~/paijiang/ && $1~/2010-11-14/) print $0}'|wc -l

ps aux| awk '{print $2}'

cat /proc/meminfo  | awk '/MemTotal/{print $2}' 内存大小

df -h | awk '{print $5}' |grep -v 'User%'    grep -v 是删除包含 User% 的行

常用的作用格式:

awk "样式" 文件

awk "{操作}" 文件

awk "样式{操作}" 文件

awk是一种用于处理数据和生成报告的编程语言。

一般的使用格式是:

   awk '{pattern + action}' {filenames} pattern指在每一行中进行匹配的条件,action指针对符合条件的行进行的操作,filenames是输入的文件名。 假设data文件中有以下数据:

   1 donald 305 20050129

   2 chin 102 20040129

   3 mark 304 20040229

下面对这个文件进行操作:

   awk '{print $1, $2, $3, $4}' data

而$1,$2...则相应代表分割后的列

逗号则让列与列之间插入空格

输出:

   1 donald 305 20050129

   2 chin 102 20040129

   3 mark 304 20040229

------------条件操作符

1 匹配 ~

awk ‘{if($1~/root/) print $0}’ /etc/passwd    #如果field1包含"root",打印该行

2.精确匹配

!=  ==

3.不匹配

!~

4.大小比较

> >= < <=

5.设置大小写 awk ‘/^[Rr]oot/’ /etc/passwd # 打印包含行首为Root或者root的行

6.任意字符 .

awk ‘$2~/^…a/’ /etc/passwd # 打印第二个字段开头第四个字母为t的行

7.或关系匹配 |

awk ‘/(root|ftp)/’ /etc/passwd #打印包含"root"或者"ftp"的行

8.AND &&    OR  ||

awk ‘{$1~/mail/ && $7==/bin/bash}’ /etc/passwd

-----------------系统变量:

   ARGV     命令行参数数组

   ENVIRON     环境变量数组

   FILENAME     当前输入文件名

   FNR     当前文件中的记录号

   FS     字段分隔符

   IGNORECASE     忽略正则表达式和串的大小写

   NF     当前记录中的字段数     NR     至今读取的记录数     OFMT     数的输出格式,缺省为"%.6g"

   OFS     输出字段分隔符

   ORS     输出记录分隔符

   RS     输入记录分隔符     RSTART     由match() 匹配的第一个字符的索引

   RLENGTH     由match() 匹配的串的长度

   SUBSEP     下标分隔符,缺省为"�34"

    内置字符串函数

   gsub(r,s,t)     在字符串t中,用字符串s替换和正则表达式r匹配的所有字符串。返回替换的个数。如果没有给出t,缺省为$0

   index(s,t)       返回s 中字符串t 的位置,不出现时为0

   length(s)     返回字符串s 的长度,当没有给出s时,返回$0的长度

   match(s,r)     返回r 在s 中出现的位置,不出现时为0。设置RSTART和RLENGTH的值

   split(s,a,r)     利用r 把s 分裂成数组a,返回元素的个数。如果没有给出r,则使用FS。数组分割和字段分割采用同样的方式

   sprintf(fmt,expr_list)       根据格式串fmt,返回经过格式编排的expr_list

   sub(r,s,t)     在字符串t中用s替换正则表达式t的首次匹配。如果成功则返回1,否则返回0。如果没有给出t,默认为$0

   substr(s,p,n)     返回字符串s中从位置p开始最大长度为n的字串。如果没有给出n,返回从p开始剩余的字符串

   tolower(s)     将串s 中的大写字母改为小写,返回新串

   toupper(s)     将串s 中的小写字母改为大写,返回新串

-----------------------awk的环境变量

变量   描述 $n   当前记录的第n个字段,字段间由FS分隔 。

$0   完整的输入记录。

ARGC   命令行参数的数目。

ARGIND   命令行中当前文件的位置(从0开始算)。

ARGV   包含命令行参数的数组。

CONVFMT   数字转换格式(默认值为%.6g)

ENVIRON   环境变量关联数组。

ERRNO   最后一个系统错误的描述。

FIELDWIDTHS   字段宽度列表(用空格键分隔)。

FILENAME   当前文件名。

FNR   同NR,但相对于当前文件。

FS   字段分隔符(默认是任何空格)。

IGNORECASE   如果为真,则进行忽略大小写的匹配。

NF   当前记录中的字段数。 NR   当前记录数。 OFMT   数字的输出格式(默认值是%.6g)。

OFS   输出字段分隔符(默认值是一个空格)。

ORS   输出记录分隔符(默认值是一个换行符)。

RLENGTH   由match函数所匹配的字符串的长度。

RS   记录分隔符(默认是一个换行符)。

RSTART   由match函数所匹配的字符串的第一个位置。

SUBSEP   数组下标分隔符(默认值是\034)。

----------------------------------gawk专用正则表达式元字符

一般通用的元字符集就不讲了,可参考我的Sed和Grep学习笔记。以下几个是gawk专用的,不适合unix版本的awk。

\Y

匹配一个单词开头或者末尾的空字符串。

\B

匹配单词内的空字符串。

\<

匹配一个单词的开头的空字符串,锚定开始。

\>

匹配一个单词的末尾的空字符串,锚定末尾。

\w

匹配一个字母数字组成的单词。

\W

匹配一个非字母数字组成的单词。

\‘

匹配字符串开头的一个空字符串。

\'

匹配字符串末尾的一个空字符串。

--------------------------------------------

 awk '/donald/ {print $4}' data       匹配当data文件中包含字符串"donald"的行,输出第4列的值:

 awk '/donald|chin/ {print $1, $2}' data      这里的"|"应该是或的作用,而不是管道,输出:

   1 donald

   2 chin

   awk '/a[rl]/ {print $1, $2}' data    兼容perl的正则表达式,匹配包含"ar"或"al"的列 ,输出:

   1 donald

   3 mark

   awk '/a[rl]/ {print $1, $2, $3+1}' data       给第三列加上1再输出:     1 donald 306

   3 mark 305

   awk '/a[rl]/ {print $1, $2} {print $3+1}' data     匹配只对第一对花括号产生作用 ,输出:

   1 donald

   306

   103

   3 mark

   305

   awk 'FS="n" {print $1}' data   使用"n"而不是空格做为分隔符,输出:

   1

   2 chi

   3 mark 304 20040229

   awk 'FS="n" {OFS="-"} {print $1, $2}' data

把分隔符输出:

   1-donald

   2 chi- 102 20040129

   3 mark 304 20040229-

   awk 'FS="n" {OFS="-"} {print NR, $1, $2}' data

使用NR变量,num of row,即行号,输出:

   1-1-donald

   2-2 chi- 102 20040129

   3-3 mark 304 20040229-

   awk '{x=x+$3} {print NR, $3, x}' data

使用变量进行累加计算,输出:

   1 305 305

   2 102 407

   3 304 711

   awk '{x=x+$3} END {print NR, $3, x}' data

使用BEGIN和END在处理之前或之后显示结果,输出:

   3 304 711

   awk '{x=x+$3} {print NR, $3, x | "sort -nr"}' data 在awk内使用管道进行排序,输出:

   3 304 711

   2 102 407

   1 305 305

   cat command

   {x=x+$3}

   {print NR, $3, x | "sort -nr"}

   awk -f command data

将指定写在文件中,输出:

   3 304 711

   2 102 407

   1 305 305

$awk '$2 == "chin" { print $3 }' 1.txt

如果简单的输出不能处理您的程序中所需要的复杂信息,则可以尝试由 printf 命令获得的更加复杂的输出,其语法是

printf( format, value, value ...) 该语法类似于 C 语言中的 printf 命令,而格式的规格是相同的。通过插入一项定义如何打印数值的规格,可以定义该格式。格式规格包含一个跟有字母的 %。类似于打印命令,printf 不必包含在圆括号中,但是可以认为使用圆括号是一种良好的习惯。

下表列出 printf 命令提供的各种规格。

规格 说明

%c 打印单个 ASCII 字符

%d 打印十进制数

%e 打印数字的科学计数表示

%f 打印浮点表示

%g 打印 %e 或 %f;两种方式都更简短

%o 打印无符号的八进制数

s 打印 ASCII 字符串

%x 打印无符号的十六进制数

%% 打印百分号;不执行转换

可以在 % 与字符之间提供某些附加的格式化参数。这些参数进一步改进数值的打印方式:

参数 说明

- 将字段中的表达式向左对齐

,width 根据需要将字段补齐到指定宽度(前导零使用零将字段补齐)

.prec 小数点右面数字的最大字符串宽度或最大数量

====================

文件disk.txt:

disk        used        noused    sum       0        209715        2097152   2306867

1        209715        2097152   2306867

2        209715        2097152   2306867

3        209715        2097152  2306867

现在我想把这个文档处理成这样

硬盘        已使用        未使用     默认大小  //英文改成中文

0        0.2G        2G         2.2G     //209715k换算成G 1M=1024k 1G=1024M 并且限制小数位为1,后面加上单位名G

1        0.2G        2G         2.2G

2        0.2G        2G         2.2G

3        0.2G        2G         2.2G ======》》

awk '{if (NR==1) {print "硬盘\t已使用\t未使用\t默认大小"} else {u=$2/1024/1024; n=$3/1024/1024; s=$4/1024/1024; p=$2/$4*100; printf("%d\t%.1fG(%.0f%)\t%.1fG\t%.1fG\n",$1,u,p,n,s)}}' disk.txt

============================

$cat file

user1  par11   par12

user2  par21   par22

user1  par31   par32

awk '{if($1=="'user1'") print $1,$2,$3}' file

这样会把结果user1  par11   par12

                 user1  par31   par32   都输出来

我想当$1匹配到第一个user1的时候,就输出结果。

=====>>>>

awk '{if($1=="'user1'") {print $1,$2,$3;exit}}' file

$ awk '{ print $0 }' /etc/passwd

在 Awk 中,变量 $0 表示整个当前行,因此 print 和 print $0 的作用完全相同。

$awk -F":" '{ print $1 $3 }' /etc/passwd halt7

operator11

root0

shutdown6

sync5

bin1

$awk -F":" '{ print "username: " $1 "\t\tuid:" $3 }' /etc/passwd

如果第一个域大于第三个域,$1就赋值给max,否则$3就赋值给max。

$ awk '{max = {$1 > $3} ? $1: $3: print max}' test

将显示test文件第一列中以root开头的行。

$ awk '$1 ~/^root/' test

如果第一和第二个域相加大于100,则打印这些行。

$ awk '$1 + $2 < 100' test。

awk '$1 > 5 && $2 < 10' test,如果第一个域大于5,并且第二个域小于10,则打印这些行。

$ awk '/root/,/mysql/' test将显示root第一次出现到mysql第一次出现之间的所有行。

============12. 一个验证passwd文件有效性的例子

1$ cat /etc/passwd | awk -F: '\

2NF != 7{\

3printf("line %d,does not have 7 fields:%s\n",NR,$0)}\

4$1 !~ /[A-Za-z0-9]/{printf("line %d,non alpha and numeric user id:%d: %s\n,NR,$0)}\

5$2 == "*" {printf("line %d, no password: %s\n",NR,$0)}'

1   cat把结果输出给awk,awk把域之间的分隔符设为冒号。

2   如果域的数量(NF)不等于7,就执行下面的程序。

3   printf打印字符串"line ?? does not have 7 fields",并显示该条记录。

4   如果第一个域没有包含任何字母和数字,printf打印“no alpha and numeric user id" ,并显示记录数和记录。

5   如果第二个域是一个星号,就打印字符串“no passwd”,紧跟着显示记录数和记录本身。

$ awk '/^(no|so)/' test-----打印所有以模式no或so开头的行。

*

$ awk '/^[ns]/{print $1}' test-----如果记录以n或s开头,就打印这个记录。

*

$ awk '$1 ~/[0-9][0-9]$/(print $1}' test-----如果第一个域以两个数字结束就打印这个记录。

*

$ awk '$1 == 100 || $2 < 50' test-----如果第一个或等于100或者第二个域小于50,则打印该行。

*

$ awk '$1 != 10' test-----如果第一个域不等于10就打印该行。

*

$ awk '/test/{print $1 + 10}' test-----如果记录包含正则表达式test,则第一个域加10并打印出来。

*

$ awk '{print ($1 > 5 ? "ok "$1: "error"$1)}' test-----如果第一个域大于5则打印问号后面的表达式值,否则打印冒号后面的表达式值。

*

$ awk '/^root/,/^mysql/' test----打印以正则表达式root开头的记录到以正则表达式mysql开头的记录范围内的所有记录。如果找到一个新的正则表达式root开头的记 录,则继续打印直到下一个以正则表达式mysql开头的记录为止,或到文件末尾。

awk '$1 ~/test/{count = $2 + $3; print count}' test,上式的作用是,awk先扫描第一个域,一旦test匹配,就把第二个域的值加上第三个域的值,并把结果赋值给变量count,最后打印出来。

$ awk '{if ($1 <$2) print $2 "too high"}' test。如果第一个域小于第二个域则打印。

$ awk '{if ($1 < $2) {count++; print "ok"}}' test.如果第一个域小于第二个域,则count加一,并打印ok。

$ awk '{if ($1 > 100) print $1 "bad" ; else print "ok"}' test。如果$1大于100则打印$1 bad,否则打印ok。

$ awk '{if ($1 > 100){ count++; print $1} else {count--; print $2}' test。如果$1大于100,则count加一,并打印$1,否则count减一,并打印$1。

====================awk有三种循环:while循环;for循环;special for循环。 *

$ awk '{ i = 1; while ( i <= NF ) { print NF,$i; i++}}' test。变量的初始值为1,若i小于可等于NF(记录中域的个数),则执行打印语句,且i增加1。直到i的值大于NF.

*

$ awk '{for (i = 1; i<NF; i++) print NF,$i}' test。作用同上。

*

breadkcontinue语句。break用于在满足条件的情况下跳出循环;continue用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端。如:

{for ( x=3; x<=NF; x++)

if ($x<0){print "Bottomed out!"; break}}

{for ( x=3; x<=NF; x++)

if ($x==0){print "Get next item"; continue}}

*

next语句从输入文件中读取一行,然后从头开始执行awk脚本。如:

{if ($1 ~/test/){next}

else {print}

}

*

exit语句用于结束awk程序,但不会略过END块。退出状态为0代表成功,非零值表示出错。

--------------------

1 、 awk '/101/' file 显示文件 file 中包含 101 的匹配行。  IXDBA.NET技术社区  awk '/101/,/105/' file awk '$1 == 5' file awk '$1 == "CT"' file 注意必须带双引号  awk '$1 * $2 >100 ' file awk '$2 >5 && $2<=15' file  2 、 awk '{print NR,NF,$1,$NF,}' file 显示文件 file 的当前记录号、域数和每一行的第一个和最后一个域。  awk '/101/ {print $1,$2 + 10}' file 显示文件 file 的匹配行的第一、二个域加 10 。  awk '/101/ {print $1$2}' file awk '/101/ {print $1 $2}' file 显示文件 file 的匹配行的第一、二个域,但显示时域中间没有分隔符。  3 、 df | awk '$4>1000000 '  通过管道符获得输入,如:显示第 4 个域满足条件的行。  4 、 awk -F "|" '{print $1}' file  按照新的分隔符 “|” 进行操作。  awk 'BEGIN { FS="[: \t|]" } {print $1,$2,$3}' file  通过设置输入分隔符( FS="[: \t|]" )修改输入分隔符。  Sep="|" awk -F $Sep '{print $1}' file 按照环境变量 Sep 的值做为分隔符。  awk -F '[ :\t|]' '{print $1}' file 按照正则表达式的值做为分隔符,这里代表空格、 : 、 TAB 、 | 同时做为分隔符。  awk -F '[][]' '{print $1}' file 按照正则表达式的值做为分隔符,这里代表 [ 、 ]  5 、 awk -f awkfile file 通过文件 awkfile 的内容依次进行控制。  cat awkfile /101/{print "\047 Hello! \047"} -- 遇到匹配行以后打印 ' Hello! '.\047 代表单引号。  {print $1,$2} -- 因为没有模式控制,打印每一行的前两个域。  6 、 awk '$1 ~ /101/ {print $1}' file  显示文件中第一个域匹配 101 的行(记录)。  7 、 awk 'BEGIN { OFS="%"} {print $1,$2}' file  通过设置输出分隔符( OFS="%" )修改输出格式。  8 、 awk 'BEGIN { max=100 ;print "max=" max} BEGIN  表示在处理任意行之前进行的操作。  {max=($1 >max ?$1:max); print $1,"Now max is "max}' file  取得文件第一个域的最大值。  (表达式 1? 表达式 2: 表达式 3 相当于:  if ( 表达式 1) 表达式 2 else 表达式 3 awk '{print ($1>4 ? "high "$1: "low "$1)}' file 9 、 awk '$1 * $2 >100 {print $1}' file  显示文件中第一个域匹配 101 的行(记录)。  10 、 awk '{$1 == 'Chi' {$3 = 'China'; print}' file  找到匹配行后先将第 3 个域替换后再显示该行(记录)。  awk '{$7 %= 3; print $7}' file  将第 7 域被 3 除,并将余数赋给第 7 域再打印。  11 、 awk '/tom/ {wage=$2+$3; printf wage}' file  找到匹配行后为变量 wage 赋值并打印该变量。  12 、 awk '/tom/ {count++;} END {print "tom was found "count" times"}' file END  表示在所有输入行处理完后进行处理。  13 、 awk 'gsub(/\$/,"");gsub(/,/,""); cost+=$4; END {print "The total is $" cost> "filename"}' file  gsub 函数用空串替换 $ 和 , 再将结果输出到 filename 中。  1 2 3 $1,200.00 1 2 3 $2,300.00 1 2 3 $4,000.00 awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>1000&&$4<2000) c1+=$4; else if ($4>2000&&$4<3000) c2+=$4; else if ($4>3000&&$4<4000) c3+=$4; else c4+=$4; } END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 通过 if 和 else if 完成条件语句  awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>3000&&$4<4000) exit; else c4+=$4; } END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 通过 exit 在某条件时退出,但是仍执行 END 操作。  awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>3000) next; else c4+=$4; } END {printf "c4=[%d]\n",c4}"' file  通过 next 在某条件时跳过该行,对下一行执行操作。  14 、 awk '{ print FILENAME,$0 }' file1 file2 file3>fileall  把 file1 、 file2 、 file3 的文件内容全部写到 fileall 中,格式为打印文件并前置文件名。  15 、 awk ' $1!=previous { close(previous); previous=$1 } {print substr($0,index($0," ") +1)>$1}' fileall   把合并后的文件重新分拆为 3 个文件。并与原文件一致。  16 、 awk 'BEGIN {"date"|getline d; print d}'  通过管道把 date 的执行结果送给 getline ,并赋给变量 d ,然后打印。  17 、 awk 'BEGIN {system("echo \"Input your name:\\c\""); getline d;print "\nYour name is",d,"\b!\n"}'  通过 getline 命令交互输入 name ,并显示出来。  awk 'BEGIN {FS=":";while(getline< "/etc/passwd" >0) { if($1~"050[0-9]_") print $1}}'  打印 /etc/passwd 文件中用户名包含 050x_ 的用户名。  18 、 awk '{ i=1;while(i  awk '{ for(i=1;i  type file|awk -F "/" ' { for(i=1;i

{ if(i==NF-1) { printf "%s",$i } else { printf "%s/",$i } })'  显示一个文件的全路径。   用 for 和 if 显示日期  awk 'BEGIN { for(j=1;j<=12;j++) { flag=0; printf "\n%d 月份 \n",j; for(i=1;i<=31;i++) { if (j==2&&i>28) flag=1; if ((j==4||j==6||j==9||j==11)&&i>30) flag=1; if (flag==0) {printf "dd ",j,i} } } }' 19 、在 awk 中调用系统变量必须用单引号,如果是双引号,则表示字符串  Flag=abcd awk '{print '$Flag'}' 结果为 abcd awk '{print "$Flag"}' 结果为 $Flag

---------------------------------awk应用小结(所有命令行均经调试)

1.调用awk: 第一种方式:命令行方式 awk [-F field-separator] 'commands' input-file(s) [-F域分隔符]是可选的,因为awk使用空格作为缺省的域分隔符,因此如果要浏览域间有空格的文本,不必指定这个选项,如果要浏览诸如passwd文件,此文件各域以冒号作为分隔符,则必须指明-F选项,如: awk -F: 'commands' input-file 第二种方式是将所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它。

第三种方式是将所有的awk命令插入一个单独文件,然后调用: awk -f awk-scrīpt-file input-files(s) -f选项指明在文件awk_scrīpt_file中的awk脚本,input_file(s)是使用awk进行浏览的文件名。 模式和动作 任何awk语句都由模式和动作组成。在一个awk脚本中可能有许多语句。模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式 部分,动作将时刻保持执行状态。模式可以是任何条件语句或复合语句或正则表达式。模式包括两个特殊字段 BEGIN和END。使用BEGIN语句设置计数和打印头。BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文本开始执行。END语 句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态标志。 域和记录 使用$1,$3表示参照第1和第3域,注意这里用逗号做域分隔。如果希望打印一个有5个域的记录的所有域,可使用$0,意即所有域。

为打印一个域或所有域,使用print命令。这是一个awk动作 模式和动作 模式 :两个特殊断 BEGIN和END 动作 :实际动作大多在{}内指明 输出 1.抽取域 awk -F: '{print $1}' /etc/passwd 2.保存输出 awk -F: '{print $1}' /etc/passwd | tee user 使用tee命令,在输出文件的同时,输出到屏幕 使用标准输出 awk -F: '{print $1}' /etc/passwd >user3 4.打印所有记录 awk -F: '{print $0}' /etc/passwd 5.打印单独记录 awk -F: '{print $1,$4}' /etc/passwd 6.打印报告头 awk -F: 'BEGIN {print "NAME\n"} {print $1}' /etc/passwd 7.打印结尾 awk -F: '{print $1} END {print "this is all users"}' /etc/passwd 条件操作符 1.匹配 awk -F: '{if($1~/root/) print }' /etc/passwd      //{if($1~/root/) print}表示如果field1包含root,打印它 2.精确匹配 使用等号 ==

awk -F: '{if($3=="0") print }' /etc/passwd  3.不匹配 !~

awk -F: '{if($1!~/linuxtone/) print }' /etc/passwd

!= 精确不匹配 awk -F: '{if($1!="linuxtone") print }' /etc/passwd 4.小于 < 5.小于等于 <= 6.大于 >

......... 7.设置大小写 awk '/[Rr]oot/' /etc/passwd 8.任意字符 awk -F: '{if($1~/^...t/) print}' /etc/passwd      //^...t表示第四个字幕是t 9.或关系匹配 awk -F: '{if ($1~/(squid|nagios)/) print}' /etc/passwd 10.行首 awk '/^root/' /etc/passwd       // ^root(行首包含root) 11.AND && awk -F: '{if($1=="root" && $3=="0") print}' /etc/passwd 12.OR || 内置变量: ARCC                                      命令行参数个数 ARGV                                      命令行参数排列 ENVIRON                                   支持队列中系统环境变量的使用 FNR                                       浏览文件的记录数 FS                                        置顶分隔符,等价于-F NF                                        浏览记录的域的个数 NR                                        已读的记录数 OFS                                       输出域分隔符 ORS                                       输出记录分隔符 RS                                        控制记录分隔符 打印有多少行记录 awk 'END {print NR}' /etc/passwd 设置输入域到变量名 awk -F: '{name=$1; path=$7; if(name~/root/) print name  "\tpath is:" path}' /etc/passwd 域值比较操作 awk '{if($6<$7) print $0}' input-file 修改文本域只显示修改改记录 awk -F: '{if($1=="nagios") {$1="nagios server" ; print }}' /etc/passwd 文件长度相加 ls -l|awk '/^[^d]/ {print $9"\t" $5} {tot+=$5} END {print "total kb:"tot}' 内置的字符窜函数 gsub(r,s)                                                      在整个$0中用s替代r gsub(r,s,t)                                                    在整个t中用s替代r index(s,t)                                                     返回s中字符串t的第一位置 length(s)                                                      返回s长度 match(s,r)                                                     测试s中是否包含匹配r的字符串 split(s,a,fs)                                                  在fs上将s分成序列a sub(s, )                                                       用$0中最左边也是最长的字符串替代 subtr(s,p)                                                     返回字符串s中从p开始的后缀部分 substr(s,p,n)                                                  返回字符串s中从p开始长度为n的后缀部分 1.gsub awk 'gsub(/^root/,"netseek") {print}' /etc/passwd             将与root开头的root替换为netseek

awk 'gsub(/0/,2) {print}' /etc/fstab awk '{print gsub(/0/,2) $0}' /etc/fstab                       2.index awk 'BEGIN {print index("root","o")}'  查询字符串root中o出现的第一位置

awk -F: '$1=="root" {print index($1,"o")" "$1}' /etc/passwd

awk -F: '{print index($1,"o") $1}' /etc/passwd 3.length awk -F: '{print length($1)'} /etc/passwd

awk -F: '$1=="root" {print length($1)}' /etc/passwd 4.match (在ANCD中查找C的位置) awk 'BEGIN {print match("ANCD",/C/)}' 5.split 返回字符串数组元素个数

awk 'BEGIN {print split("123#456#789", myarray, "#")}' 6.sub   只能替换指定域的第一个0 awk 'sub(/0/,2) {print }' /etc/fstab 7.substr  按照起始位置及长度返回字符串的一部分

awk 'BEGIN {print substr("www.linuxtone.org",5,9)}'  //第5个字符开始,取9个字符。

awk 'BEGIN {print substr("www.linuxtone.org",5)}'  //第5个位置开始,取后面的所有. 字符串屏蔽序列 \b                  退格键 \f                  走纸换页 \n                  新行 \r                  回车 \t                  tab \c                  任意其他特殊字符 \ddd                八进制 很简单的例子

awk -F: '{print $1,"\b"$2,"\t"$3}' /etc/passwd 输出函数printf(注意是printf不是print,两者效果不同的)

printf函数拥有几种不同的格式化输出功能 printf修饰符 -                               左对齐 Width                        域的步长0表示0步长 .prec                          最大字符串长度,或小数点右边的位数 awk printf格式 %c                              ASCII字符 %d                              整数 %e                              浮点数,科学计数法 %f                               浮点数 %g                              awk决定使用哪种浮点数转换,e或者f %o                              八进制数 %s                              字符串 %x                              十六进制数 1.字符串转换 echo "65" | awk '{printf"%c\n",$0}'

awk 'BEGIN {printf "%c\n",65}'

awk 'BEGIN {printf "%f\n",999}' 2.格式化输出 awk -F: '{printf "%-15s %s\n",$1,$3}' /etc/passwd

awk -F: 'BEGIN {printf"USER\t\tUID\n"} {printf "%-15s %s\n",$1,$3}' /etc/passwd 3.向一行awk命令传值 who | awk '{if ($1 == user) print $1 " you are connected to:" $2}' user=$LOGNAME 4.awk脚本文件 (在文件名字后面加后缀.awk方便区分) #!/bin/awk -f

BEGIN{

   FS=":"

       print "User\t\tUID"

       print"____________________________"

}

{printf "%-15s %s\n",$1,$3}

END{

      print "END"

}


0 0