服务器 频道

AWK :Linux 管理员的智能工具包(中)

  【IT168 服务器学院】使用模式

  通过包含一个必须匹配的模式,您可以选择只对特定的记录而不是所有的记录进行操作。模式匹配的最简单形式是搜索,其中要匹配的项目被包含在斜线 ( /pattern/ ) 中。例如,只对那些居住在阿拉巴马州的员工执行前面的操作:

  $ awk ''/AL/ {print $3,$2}'' emp_names

  EVAN DULANEY

  JEFF DURHAM

  BILL STEEN

  EVAN FELDMAN

  STEVE SWIM

  $

  如果您不指定要打印的字段,则会打印整个匹配的条目:

  $ awk ''/AL/'' emp_names

  46012 DULANEY EVAN MOBILE AL

  46013 DURHAM JEFF MOBILE AL

  46015 STEEN BILL MOBILE AL

  46017 FELDMAN EVAN MOBILE AL

  46018 SWIM STEVE UNKNOWN AL

  $

  对同一数据集的多个命令可以用分号 ( ; ) 分隔开。例如,在一行中打印姓名,而在另一行中打印城市和州名:

  $ awk ''/AL/ {print $3,$2 ; print $4,$5}'' emp_names

  EVAN DULANEY

  MOBILE AL

  JEFF DURHAM

  MOBILE AL

  BILL STEEN

  MOBILE AL

  EVAN FELDMAN

  MOBILE AL

  STEVE SWIM

  UNKNOWN AL

  $

  如果没有使用分号 ( print $3,$2,$4,$5 ) ,则会在同一行中显示所有内容。另一方面,如果分别给出两个打印语句,则会产生完全不同的结果:

  $ awk ''/AL/ {print $3,$2} {print $4,$5}'' emp_names

  EVAN DULANEY

  MOBILE AL

  JEFF DURHAM

  MOBILE AL

  BILL STEEN

  MOBILE AL

  EVAN FELDMAN

  MOBILE AL

  STEVE SWIM

  UNKNOWN AL

  PHOENIX AZ

  PHOENIX AZ

  UNKNOWN AR

  MUNCIE IN

  MUNCIE IN

  MUNCIE IN

  MUNCIE IN

  $

  只有在列表中找到 AL 时才会给出字段三和字段二。但是,字段四和字段五是无条件的,始终打印它们。只有第一组花括号中的命令对前面紧邻的命令 ( /AL/ ) 起作用。

  结果非常不便于阅读,可以使其稍微更清晰一些。首先,在城市与州之间插入一个空格和逗号。然后,在每两行显示之后放置一个空行:

  $ awk ''/AL/ {print $3,$2 ; print $4", "$5"\n"}'' emp_names

  EVAN DULANEY

  MOBILE , AL

  JEFF DURHAM

  MOBILE , AL

  

  BILL STEEN

  MOBILE , AL

  

  EVAN FELDMAN

  MOBILE , AL

  

  STEVE SWIM

  UNKNOWN, AL

  $

  在第四和第五个字段之间,添加一个逗号和一个空格(在引号之间),在第五个字段后面,打印一个换行符 ( \n ) 。在 AWK 打印语句中还可以使用那些可在 echo 命令中使用的所有特殊字符,包括:

  • \n (换行)
  • \t (制表)
  • \b (退格)
  • \f (进纸)
  • \r (回车)

  因此,要读取全部五个最初由制表符分隔开的字段,并且也利用制表符打印它们,您可以编程如下

  $ awk ''{print $1"\t"$2"\t"$3"\t"$4"\t"$5}'' emp_names

  46012 DULANEY EVAN MOBILE AL

  46013 DURHAM JEFF MOBILE AL

  46015 STEEN BILL MOBILE AL

  46017 FELDMAN EVAN MOBILE AL

  46018 SWIM STEVE UNKNOWN AL

  46019 BOGUE ROBERT PHOENIX AZ

  46021 JUNE MICAH PHOENIX AZ

  46022 KANE SHERYL UNKNOWN AR

  46024 WOOD WILLIAM MUNCIE IN

  46026 FERGUS SARAH MUNCIE IN

  46027 BUCK SARAH MUNCIE IN

  46029 TUTTLE BOB MUNCIE IN

  $

  通过连续设置多项标准并用管道 ( | ) 符号将其分隔开,您可以一次搜索多个模式匹配:

  $ awk ''/AL|IN/'' emp_names

  46012 DULANEY EVAN MOBILE AL

  46013 DURHAM JEFF MOBILE AL

  46015 STEEN BILL MOBILE AL

  46017 FELDMAN EVAN MOBILE AL

  46018 SWIM STEVE UNKNOWN AL

  46024 WOOD WILLIAM MUNCIE IN

  46026 FERGUS SARAH MUNCIE IN

  46027 BUCK SARAH MUNCIE IN

  46029 TUTTLE BOB MUNCIE IN

  $

  这样可找到每个阿拉巴马州和印第安那州居民的匹配记录。但是在试图找出居住在亚利桑那州的人时,出现了一个问题:

  $ awk ''/AR/'' emp_names

  46019 BOGUE ROBERT PHOENIX AZ

  46021 JUNE MICAH PHOENIX AZ

  46022 KANE SHERYL UNKNOWN AZ

  46026 FERGUS SARAH MUNCIE IN

  46027 BUCK SARAH MUNCIE IN

  $

  员工 46026 和 46027 没有住在亚利桑那州;但是他们的名字中包含所搜索的字符序列。切记,当在 AWK 中进行模式匹配时,例如 grep 、 sed 或者大部分其他 Linux/Unix 命令,将在记录(行)中的任何位置查找匹配,除非指定进行其他操作。为解决这一问题,必须将搜索与特定字段联系起来。通过利用代字号 (?) 以及对特定字段的说明,可以达到这一目的,如下例所示:

  $ awk ''$5 ? /AR/'' emp_names

  46019 BOGUE ROBERT PHOENIX AZ

  46021 JUNE MICAH PHOENIX AZ

  46022 KANE SHERYL UNKNOWN AZ

  $

  代字号(表示匹配)的对应符号是一个前面带有感叹号的代字号 (!?) 。这些字符通知程序,如果搜索序列没有出现在指定字段中,则找出与搜索序列相匹配的所有行:

  $ awk ''$5 !? /AR/'' names

  46012 DULANEY EVAN MOBILE AL

  46013 DURHAM JEFF MOBILE AL

  46015 STEEN BILL MOBILE AL

  46017 FELDMAN EVAN MOBILE AL

  46018 SWIM STEVE UNKNOWN AL

  46024 WOOD WILLIAM MUNCIE IN

  46026 FERGUS SARAH MUNCIE IN

  46027 BUCK SARAH MUNCIE IN

  46029 TUTTLE BOB MUNCIE IN

  $

  在这种情况下,将显示第五个字段中没有 AR 的所有行 — 包括两个 Sarah 条目,这两个条目确实包含 AR ,但却是在第三个字段而不是第五个字段中。

  花括号和字段分隔符

  括号字符在 AWK 命令中起着很重要的作用。出现在括号之间的操作指出将要发生什么以及何时发生。当只使用一对括号时:

  {print $3,$2}

  括号间的所有操作同时发生。当使用多于一对的括号时:

  {print $3}{print $2}

  执行第一组命令,在该命令完成后执行第二组命令。注意以下两列清单的区别:

  $ awk ''{print $3,$2}'' names

  EVAN DULANEY

  JEFF DURHAM

  BILL STEEN

  EVAN FELDMAN

  STEVE SWIM

  ROBERT BOGUE

  MICAH JUNE

  SHERYL KANE

  WILLIAM WOOD

  SARAH FERGUS

  SARAH BUCK

  BOB TUTTLE

  $

  

  $ awk ''{print $3}{print $2}'' names

  EVAN

  DULANEY

  JEFF

  DURHAM

  BILL

  STEEN

  EVAN

  FELDMAN

  STEVE

  SWIM

  ROBERT

  BOGUE

  MICAH

  JUNE

  SHERYL

  KANE

  WILLIAM

  WOOD

  SARAH

  FERGUS

  SARAH

  BUCK

  BOB

  TUTTLE

  $

  要利用多组括号进行重复查找,执行第一组中的命令直到完成为止;然后处理第二组命令。如果有第三组命令,则在第二组命令完成后执行它,以此类推。在所生成的打印输出中,有两个分隔的打印命令,因此先执行第一个命令,随后执行第二个命令,这样导致每个条目显示在两行而不是一行中。

  区分两个字段的字段分隔符不一定始终是空格;它可以是任何可识别的字符。为进行演示,假定 emp_names 文件利用冒号而不是制表符来分隔字段:

  $ cat emp_names

  46012:DULANEY:EVAN: MOBILE : AL

  46013: DURHAM :JEFF: MOBILE : AL

  46015:STEEN:BILL: MOBILE : AL

  46017:FELDMAN:EVAN: MOBILE : AL

  46018:SWIM:STEVE:UNKNOWN: AL

  46019:BOGUE:ROBERT: PHOENIX :AZ

  46021:JUNE:MICAH: PHOENIX :AZ

  46022:KANE:SHERYL:UNKNOWN:AR

  46024:WOOD:WILLIAM: MUNCIE :IN

  46026:FERGUS:SARAH: MUNCIE :IN

  46027:BUCK:SARAH: MUNCIE :IN

  46029:TUTTLE:BOB: MUNCIE :IN

  $

  如果试图通过指定所需要的第二个字段来打印姓氏

  $ awk ''{print $2}'' emp_names

  您最后会得到十二个空行。因为文件中没有空格,除了第一个字段之外没有可认别的字段。为解决这一问题,必须通知 AWK 是空格之外的另一个字符作为分隔符,有两种方法可通知 AWK 使用新的字段分隔符:使用命令行参数 -F ,或在程序中指定变量 FS 。两种方法的效果相同,只有一种例外情况,如下例所示:

  $ awk ''{FS=":"}{print $2}'' emp_names

  

  DURHAM

  STEEN

  FELDMAN

  SWIM

  BOGUE

  JUNE

  KANE

  WOOD

  FERGUS

  BUCK

  TUTTLE

  $

  

  $ awk -F: ''{print $2}'' emp_names

  DULANEY

  DURHAM

  STEEN

  FELDMAN

  SWIM

  BOGUE

  JUNE

  KANE

  WOOD

  FERGUS

  BUCK

  TUTTLE

  $

  在第一个命令中,头一条记录返回不正确的空行,而其他结果正确。直到读取第二条记录时,才识别字段分隔符并正确地执行。通过使用 BEGIN 语句可以纠正这一缺点(在后文详述)。 -F 的功能非常类似于 BEGIN ,能够正确地读取第一条记录并按要求执行。

  在本文开始处我曾提到,默认的显示 / 输出字段分隔符是空格。通过使用输出字段分隔符 ( OFS ) 变量,可以在程序中更改此特性。例如,要读取文件(由冒号分隔)并以短划线显示,则命令是

  $ awk -F":" ''{OFS="-"}{print $1,$2,$3,$4,$5}'' emp_names

  46012-DULANEY-EVAN-MOBILE-AL

  46013-DURHAM-JEFF-MOBILE-AL

  46015-STEEN-BILL-MOBILE-AL

  46017-FELDMAN-EVAN-MOBILE-AL

  46018-SWIM-STEVE-UNKNOWN-AL

  46019-BOGUE-ROBERT-PHOENIX-AZ

  46021-JUNE-MICAH-PHOENIX-AZ

  46022-KANE-SHERYL-UNKNOWN-AR

  46024-WOOD-WILLIAM-MUNCIE-IN

  46026-FERGUS-SARAH-MUNCIE-IN

  46027-BUCK-SARAH-MUNCIE-IN

  46029-TUTTLE-BOB-MUNCIE-IN

  $

  FS 和 OFS 是(输入)字段分隔符和输出字段分隔符,它们只是一对可以在 AWK 实用工具中使用的变量。例如,要在打印时为每行编号,可以采用以下方式使用 NR 变量:

  $ awk -F":" ''{print NR,$1,$2,$3}'' emp_names

  1 46012 DULANEY EVAN

  2 46013 DURHAM JEFF

  3 46015 STEEN BILL

  4 46017 FELDMAN EVAN

  5 46018 SWIM STEVE

  6 46019 BOGUE ROBERT

  7 46021 JUNE MICAH

  8 46022 KANE SHERYL

  9 46024 WOOD WILLIAM

  10 46026 FERGUS SARAH

  11 46027 BUCK SARAH

  12 46029 TUTTLE BOB

  $

  找出员工号码处于 46012 和 46015 之间的所有行:

  $ awk -F":" ''/4601[2-5]/'' emp_names

  46012 DULANEY EVAN MOBILE AL

  46013 DURHAM JEFF MOBILE AL

  46015 STEEN BILL MOBILE AL

  $

0
相关文章