awk基础知识小结(1)

一、使用规则
awk 适合于文本处理和报表生成,它还有许多精心设计的特性,容许进行须要特殊技巧程序设计。
awk 
的语法较为常见。它借鉴了某些语言的一些精华部分,如C 语言、python 和 bash。
第一个 awk
让咱们继续,开始使用 awk,以了解其工做原理。在命令行中输入如下命令:
$ awk '{ print }' 
/etc/passwd
您将会见到 /etc/passwd 文件的内容出如今眼前。如今,解释 awk 作了些什么。调用 awk 时,咱们指定 
/etc/passwd 做为输入文件。执行 awk 时,它依次对 /etc/passwd 中的每一行执行 print 命令。全部输出都发送到 
stdout,所获得的结果与与执行catting /etc/passwd彻底相同。
如今,解释 { print } 代码块。在 awk 中,花括号用于将几块代码组合到一块儿,这一点相似于 C 语言。在代码块中只有一条 print 命令。在 
awk 中,若是只出现 print 命令,那么将打印当前行的所有内容。
这里是另外一个awk 示例,做用与上例彻底相同:
$ awk '{ print $0 }' /etc/passwd
在 awk 中,$0 变量表示整个当前行,因此 print 和 print $0 的做用彻底同样。
建立一个 awk 程序,让它输出与输入数据彻底无关的数据。
示例1:
$ awk '{ print "" }' 
/etc/passwd
只要将 "" 字符串传递给 print 
命令,它就会打印空白行。测试该脚本,将会发现对于/etc/passwd文件中的每一行,awk 
都输出一个空白行。由此可知,awk对输入文件中的每一行都执行这个脚本。
示例2:
$ awk '{ print "hiya" }' /etc/passwd
运行此脚本将在您的屏幕上写满 hiya。
二、处理多个字段
awk 很是善于处理分红多个逻辑字段的文本,还能够引用 awk 
脚本中每一个独立的字段。
打印系统上全部用户账户的列表:
$ awk -F":" '{ print $1 }' 
/etc/passwd
上例中,调用awk时,使用 -F 选项来指定 ":" 做为字段分隔符。awk 处理 print $1 
命令时,它会打印出在输入文件中每一行中出现的第一个字段。
如下是另外一示例:
$ awk -F":" '{ print $1 $3 }' 
/etc/passwd
如下是该脚本输出的摘录:
halt7
operator11
root0
shutdown6
sync5
bin1
....etc.
如您所见,awk 打印出 /etc/passwd 
文件的第一和第三个字段,它们正好分别是用户名和用户标识字段。如今,当脚本运行时,它并不理想--在两个输出字段之间没有空格!若是习惯于使用 bash 或 
python 进行编程,那么您会期望 print $1 $3 命令在两个字段之间插入空格。然而,当两个字符串在 awk 程序中彼此相邻时,awk 
会链接它们但不在它们之间添加空格。如下命令会在这两个字段中插入空格:
$ awk -F":" '{ print $1 " " $3 }' 
/etc/passwd
以这种方式调用 print 时,它将链接 $一、" " 和 $3,建立可读的输出。
还能够插入一些文本标签:
$ awk -F":" '{ 
print "username: " $1 "ttuid:" $3" }' /etc/passwd
这将产生如下输出:
username: halt          uid:7
username: operator      
uid:11
username: root          uid:0
username: shutdown      
uid:6
username: sync          uid:5
username: bin           
uid:1
....etc.
三、调用外部脚本
将脚本做为命令行自变量传递给awk对于小的单行程序来讲很简单。
而对于多行程序,则能够在外部文件中撰写脚本,而后向awk传递-f选项,以向它提供外部脚本文件的调用:
$ 
awk -f myscript.awk myfile.in
将脚本放入文本文件还可使用附加awk功能。例如:
BEGIN {
      FS=":"
}
{ print $1 
}
打印出 /etc/passwd 中每一行的第一个字段
在这个脚本中,字段分隔符在代码自身中指定(经过设置 FS 变量)。
在脚本自身中设置字段分隔符,能够少输入一个命令行自变量。
四、begin和end块
BEGIN 和 END 块
一般,对于每一个输入行,awk 都会执行每一个脚本代码块一次。然而,可能须要在 awk 
开始处理输入文件中的文本以前执行初始化代码。对于这种状况,awk 容许您定义一个 BEGIN 块。咱们在前一个示例中使用了 BEGIN 块。由于 awk 
在开始处理输入文件以前会执行 BEGIN 块,所以它是初始化 FS(字段分隔符)变量、打印页眉或初始化其它在程序中之后会引用的全局变量的极佳位置。
awk 还提供了另外一个特殊块,叫做 END 块。awk 在处理了输入文件中的全部行以后执行这个块。一般,END 
块用于执行最终计算或打印应该出如今输出流结尾的摘要信息。
五、正则表达式
awk 
容许使用正则表达式,根据正则表达式是否匹配当前行来选择执行独立代码块。
输出包含字符序列foo的行:
/foo/ { print }
复杂点的,只打印包含浮点数的行:
/[0-9]+.[0-9]*/ { print }
能够将任意一种布尔表达式放在一个代码块以前,以控制什么时候执行某特定块。仅当对前面的布尔表达式求值为真时,awk 
才执行代码块。如下示例脚本输出将输出其第一个字段等于 fred 的全部行中的第三个字段。若是当前行的第一个字段不等于 fred,awk 
将继续处理文件而不对当前行执行 print 语句:
$1 == "fred" { print $3 }
awk 提供了完整的比较运算符集合,包括 "=="、"<"、">"、"<="、">=" 和 "!="。另外,awk 还提供了 
"~" 和 "!~" 
运算符,它们分别表示“匹配”和“不匹配”。
它们的用法是在运算符左边指定变量,在右边指定正则表达式。若是某一行的第五个字段包含字符序列 
root,如下示例只打印这一行中的第三个字段:
$5 ~ /root/ { print $3 }
六、条件语句
awk 还提供了很是好的相似于 C 语言的 if 语句。if 语句示例:
{ 
    if ( $5 ~ /root/ ) { 
       print $3 
    }
}
对每个输入行执行代码块,使用 if 
语句来选择执行 print 命令。
更复杂的 awk if 语句示例。
{
    if ( $1 == "foo" ) 
{
       if ( $2 == "foo" ) {
            print "uno"
        } else 
{
          print "one"
        }
          } else if ($1 == "bar" ) 
{
                    print "two"
                 } else 
{
                    print "three"
                 }
}
使用 if 语句还能够将代码:
! /matchme/ { print $1 $3 $4 }
转换成:
{
     if ( 
$0 !~ /matchme/ ) {
         print $1 $3 $4
     }
}
这两个脚本都只输出不包含 
matchme 字符序列的那些行。
awk 还容许使用布尔运算符 "||"(逻辑与)和 "&&"(逻辑或),以便建立更复杂的布尔表达式:
( $1 == "foo" ) 
&& ( $2 == "bar" ) { print } 
这个示例只打印第一个字段等于 foo 且第二个字段等于 bar 的行。
七、变量
awk的变量,数值变量与字符串变量。
数值变量
至今,咱们不是打印字符串、整行就是特定字段。然而,awk还能够执行整数和浮点运算。使用数学表达式,能够很方便地编写计算文件中空白行数量的脚本。
BEGIN   
{ x=0 }
/^$/    { x=x+1 }
END     { print "I found " x " blank lines. :}" 
}
在 BEGIN 块中,将整数变量 x 初始化成零。而后,awk 每次遇到空白行时,awk 将执行 x=x+1 语句,递增 
x。
处理完全部行以后,执行 END 块,awk 将打印出最终摘要,指出它找到的空白行数量。
字符串化变量
awk 的优势之一就是“简单和字符串化”。我认为 awk 变量“字符串化”是由于全部 awk 
变量在内部都是按字符串形式存储的。同时,awk 变量是“简单的”,由于能够对它执行数学操做,且只要变量包含有效数字字符串,awk 
会自动处理字符串到数字的转换步骤。要理解个人观点,请研究如下示例:
x="1.01"
# We just set x to contain the 
*string* "1.01"
x=x+1
# We just added one to a *string* 
print x
# 
Incidentally, these are comments :)
awk 将输出:
2.01
虽然将字符串值 1.01 赋值给变量 x,仍然能够对它加一。但在 bash 和 python 中却不能这样作。
首先,bash 
不支持浮点运算。并且,若是 bash 有“字符串化”变量,它们并不“简单”;要执行任何数学操做,bash 要求咱们将数字放到丑陋的 $( ) ) 
结构中。
若是使用 python,则必须在对 1.01 字符串执行任何数学运算以前,将它转换成浮点值。虽然这并不困难,但它还是附加的步骤。
若是使用 
awk,它是全自动的,而那会使咱们的代码又好又整洁。若是想要对每一个输入行的第一个字段乘方并加一,可使用如下脚本:
{ print ($1^2)+1 
}
若是作一个小实验,就能够发现若是某个特定变量不包含有效数字,awk 在对数学表达式求值时会将该变量看成数字零处理。
八、运算符
awk 有完整的数学运算符集合。除了标准的加、减、乘、除,awk 还容许使用前面演示过的指数运算符 
"^"、模(余数)运算符 "%" 和其它许多从 C 语言中借入的易于使用的赋值操做符。
这些运算符包括先后加减(i++、--foo)、加/减/乘/除赋值运算符( a+=三、b*=二、c/=2.二、d-=6.2)。不只如此 -- 
咱们还有易于使用的模/指数赋值运算符(a^=二、b%=4)。
字段分隔符
awk 有它本身的特殊变量集合。其中一些容许调整 awk 
的运行方式,而其它变量能够被读取以收集关于输入的有用信息。咱们已经接触过这些特殊变量中的一个,FS。前面已经提到过,这个变量让您能够设置 awk 
要查找的字段之间的字符序列。咱们使用 /etc/passwd 做为输入时,将 FS 设置成 ":"。当这样作有问题时,咱们还能够更灵活地使用 FS。
FS 值并无被限制为单一字符;能够经过指定任意长度的字符模式,将它设置成规则表达式。若是正在处理由一个或多个 tab 
分隔的字段,您可能但愿按如下方式设置 FS:
FS="t+"
以上示例中,咱们使用特殊 "+" 规则表达式字符,它表示“一个或多个前一字符”。
若是字段由空格分隔(一个或多个空格或 tab),您可能想要将 FS 设置成如下规则表达式:
FS="[[:space:]+]"
这个赋值表达式也有问题,它并不是必要。为何?由于缺省状况下,FS 设置成单一空格字符,awk 将这解释成表示“一个或多个空格或 
tab”。在这个特殊示例中,缺省 FS 设置偏偏是您最想要的!
复杂的规则表达式也不成问题。即便您的记录由单词 "foo" 分隔,后面跟着三个数字,如下规则表达式仍容许对数据进行正确的分析:
FS="foo[0-9][0-9][0-9]"
字段数量
接着咱们要讨论的两个变量一般并非须要赋值的,而是用来读取以获取关于输入的有用信息。第一个是 NF 变量,也叫作“字段数量”变量。awk 
会自动将该变量设置成当前记录中的字段数量。可使用 NF 变量来只显示某些输入行:
NF == 3 { print "this particular 
record has three fields: " $0 }
固然,也能够在条件语句中使用 NF 变量,以下:
{   
    if ( 
NF > 2 ) {
       print $1 " " $2 ":" $3 
    }
}
九、处理记录
记录号
记录号 (NR) 是另外一个方便的变量。它始终包含当前记录的编号(awk 
将第一个记录算做记录号 1)。迄今为止,咱们已经处理了每一行包含一个记录的输入文件。对于这些状况,NR 
还会告诉您当前行号。然而,当咱们在本系列之后部分中开始处理多行记录时,就不会再有这种状况,因此要注意!能够象使用 NF 变量同样使用 NR 
来只打印某些输入行:
(NR < 10 ) || (NR > 100) { print "We are on record number 
1-9 or 101+" }
另外一个示例:
{
   #skip header
   if (NR>10) {
       print "ok, now for the real information!"
   }
}
awk 提供了适合各类用途的附加变量。咱们将在之后的文章中讨论这些变量。
多行记录
awk 是一种用于读取和处理结构化数据(如系统的 /etc/passwd 文件)的极佳工具。/etc/passwd 是 UNIX 
用户数据库,而且是用冒号定界的文本文件,它包含许多重要信息,包括全部现有用户账户和用户标识,以及其它信息。在个人前一篇文章中,我演示了 awk 
如何轻松地分析这个文件。咱们只须将 FS(字段分隔符)变量设置成 ":"。
正确设置了 FS 变量以后,就能够将 awk 配置成分析几乎任何类型的结构化数据,只要这些数据是每行一个记录。然而,若是要分析占据多行的记录,仅仅依靠设置 
FS 是不够的。在这些状况下,咱们还须要修改 RS 记录分隔符变量。RS 变量告诉 awk 当前记录何时结束,新记录何时开始。
譬如,让咱们讨论一下如何完成处理“联邦证人保护计划”所涉及人员的地址列表的任务: 
Jimmy the Weasel
100 Pleasant 
Drive
San Francisco, CA 12345
Big Tony
200 Incognito Ave.
Suburbia, 
WA 67890
理论上,咱们但愿 awk 将每 3 行看做是一个独立的记录,而不是三个独立的记录。若是 awk 将地址的第一行看做是第一个字段 
($1),街道地址看做是第二个字段 ($2),城市、州和邮政编码看做是第三个字段 $3,那么这个代码就会变得很简单。代码以下: 
BEGIN {
  FS="n"
  RS=""
}
在上面这段代码中,将 FS 设置成 "n" 告诉 awk 每一个字段都占据一行。经过将 RS 设置成 "",还会告诉 awk 
每一个地址记录都由空白行分隔。一旦 awk 
知道是如何格式化输入的,它就能够为咱们执行全部分析工做,脚本的其他部分很简单。让咱们研究一个完整的脚本,它将分析这个地址列表,并将每一个记录打印在一行上,用逗号分隔每一个字段。
address.awk 
BEGIN {
   FS="n"
   RS=""
}
{
  print $1 ", " $2 ", " 
$3
}
将脚本保存为 address.awk,地址数据存储在文件 address.txt 中,能够经过输入 "awk -f address.awk 
address.txt" 执行此脚本。输出以下: 
Jimmy the Weasel, 100 Pleasant Drive, San 
Francisco, CA 12345
Big Tony, 200 Incognito Ave., Suburbia, WA 67890
OFS 和 ORS
在 address.awk 的 print 语句中,能够看到 awk 
会链接(合并)一行中彼此相邻的字符串。咱们使用此功能在同一行上的三个字段之间插入一个逗号和空格 (", ")。这个方法虽然有用,但比较难看。与其在字段间插入 
", " 字符串,倒不如让经过设置一个特殊 awk 变量 OFS,让 awk 完成这件事。
print "Hello", "there", 
"Jim!"
这行代码中的逗号并非实际文字字符串的一部分。事实上,它们告诉 awk "Hello"、"there" 和 "Jim!" 
是单独的字段,而且应该在每一个字符串之间打印 OFS 变量。
缺省状况下,awk 产生如下输出: 
Hello there Jim!
这是缺省状况下的输出结果,OFS 被设置成 " ",单个空格。不过,咱们能够方便地从新定义 OFS,这样 awk 将插入咱们中意的字段分隔符。如下是原始 
address.awk 程序的修订版,它使用 OFS 来输出那些中间的 ", " 字符串:
address.awk 的修订版 
BEGIN {
     FS="n"
     RS=""
    OFS=", 
"
}
{
    print $1, $2, $3
}
 awk 还有一个特殊变量 
ORS,全称是“输出记录分隔符”。经过设置缺省为换行 ("n") 的 OFS,咱们能够控制在 print 语句结尾自动打印的字符。缺省 ORS 值会使 awk 
在新行中输出每一个新的 print 语句。若是想使输出的间隔翻倍,能够将 ORS 设置成 "nn"。或者,若是想要用单个空格分隔记录(而不换行),将 ORS 
设置成 " "。
将多行转换成用 tab 分隔的格式
假设咱们编写了一个脚本,它将地址列表转换成每一个记录一行,且用 tab 
定界的格式,以便导入电子表格。使用稍加修改的 address.awk 以后,就能够清楚地看到这个程序只适合于三行的地址。若是 awk 
遇到如下地址,将丢掉第四行,而且不打印该行:
Cousin Vinnie
Vinnie's Auto Shop
300 City 
Alley
Sosueme, OR 76543
要处理这种状况,代码最好考虑每一个字段的记录数量,并依次打印每一个记录。如今,代码只打印地址的前三个字段。如下就是咱们想要的一些代码:
适合具备任意多字段的地址的 address.awk 版本
BEGIN { 
  FS="n" 
  RS="" 
  ORS=""
} 
 {  
 x=1 
 while ( x<NF ) { 
 print $x "t" 
 x++ 
  } 
print $NF "n" 
}
首先,将字段分隔符 FS 设置成 "n",将记录分隔符 RS 设置成 "",这样 awk 能够象之前同样正确分析多行地址。而后,将输出记录分隔符 ORS 
设置成 "",它将使 print 语句在每一个调用结尾不输出新行。这意味着若是但愿任何文本重新的一行开始,那么须要明确写入 print "n"。
在主代码块中,建立了一个变量 x 来存储正在处理的当前字段的编号。起初,它被设置成 1。而后,咱们使用 while 循环(一种 awk 循环结构,等同于 
C 语言中的 while 循环),对于全部记录(最后一个记录除外)重复打印记录和 tab 字符。最后,打印最后一个记录和换行;此外,因为将 ORS 设置成 
"",print 将不输出换行。程序输出以下,这正是咱们所指望的(不算漂亮,但用 tab 定界,以便于导入电子表格):
Jimmy the 
Weasel        100 Pleasant Drive      San Francisco, CA 12345 
Big 
Tony        200 Incognito Ave.      Suburbia, WA 67890
Cousin Vinnie   
Vinnie's Auto Shop      300 City Alley  Sosueme, OR 76543
相关文章
相关标签/搜索