跳转到内容

Awk 入门/Awk 程序文件

来自 Wikibooks,开放书籍,开放世界

有时 Awk 程序需要反复使用。在这种情况下,从 shell 脚本执行 Awk 程序非常简单。例如,考虑一个将文件中的每个单词打印在单独一行的 Awk 脚本。这可以使用名为“words”的脚本完成,其中包含

   awk '{c=split($0, s); for(n=1; n<=c; ++n) print s[n] }' $1

然后可以使“Words”可执行(使用“chmod +x words”),并像任何其他命令一样调用生成的 shell“程序”。例如,

“words”可以从“vi”文本编辑器中调用,如下所示

   :%!words

这会将所有文本变成一个单字列表。

  • 再举一个例子,考虑前面提到的双倍行距程序。这可以稍微更改以接受标准输入,使用前面描述的“-”,然后复制到名为“double”的文件中
   awk '{print; if (NF != 0) print ""}' -

—然后可以从“vi”调用它来对编辑器中的所有文本进行双倍行距。

  • 下一步将是允许“double”执行相反的操作:将双倍行距文件恢复为单倍行距,使用选项
   undouble

当然,任务的第一部分是设计一种方法来删除多余的空行,而不通过删除所有空行来破坏原始单倍行距文件的间距。最简单的方法是删除连续空行块中的每隔一行。这并不一定能保留原始间距,但它会以某种形式保留间距。

实现此方法也很简单,涉及使用名为“skip”的变量。每当跳过空行时,此变量被设置为“1”,以告诉 Awk 程序不要跳过下一行。方案如下

   BEGIN {set skip to 0}
   scan the input:
      if skip == 0    if line is blank
                         skip = 1
                      else
                         print the line
                      get next line of input
      if skip == 1    print the line
                      skip = 0
                      get next line of input

这直接转换为以下 Awk 程序

   BEGIN      {skip = 0}
   skip == 0  {if (NF == 0) 
                {skip = 1} 
               else 
                {print}; 
               next}
   skip == 1  {print; 
               skip = 0;
               next}

该程序可以放在一个单独的文件中,命名为“undouble.awk”,并编写 shell 脚本“undouble”为

   awk -f undouble.awk

它也可以直接嵌入到 shell 脚本中,使用单引号将程序括起来,并使用反斜杠(“\”)允许多行

   awk 'BEGIN      {skip = 0} \
        skip == 0  {if (NF == 0) 
                     {skip = 1}  \
                    else 
                     {print};  \
                    next} \
        skip == 1  {print; \
                    skip = 0; \
                    next}'

请记住,当使用“\”将 Awk 程序嵌入到脚本文件中时,该程序在 Awk 中显示为一行。必须使用分号分隔命令。

再举一个更复杂的例子,我遇到一个问题,当我编写文本文档时,有时我会意外地输入两次同一个词:“结果也是也是那样......”。这些重复的词在校对时很难发现,但编写一个 Awk 程序来完成这项工作很简单,它扫描文本文件以查找重复项;如果找到重复项,则打印重复的词和它所在的行;否则打印“未找到重复项”。

   BEGIN { dups=0; w="xy-zzy" }
         { for( n=1; n<=NF; n++) 
              { if ( w == $n ) { print w, "::", $0 ; dups = 1 } ; w = $n }
         } 
   END   { if (dups == 0) print "No duplicates found." }

“w”变量存储文件中每个词,将其与文件中的下一个词进行比较;w 被初始化为“xy-zzy”,因为这不太可能是文件中的一个词。变量“dup”被初始化为 0,如果找到重复项则设置为 1;如果在结尾时仍然为 0,则程序打印“未找到重复项”消息。与前面的示例一样,我们可以将其放入一个单独的文件中,或者将其嵌入到脚本文件中。

  • 最后这些示例使用变量来允许 Awk 程序跟踪它一直在做什么。如前所述,Awk 以循环方式运行:获取一行,处理它,获取下一行,处理它,等等;为了使 Awk 程序记住循环之间的内容,它需要在变量中留下一个便条。

例如,假设我们要匹配第一字段值为 1,000 的行,但随后打印下一行。我们可以这样做:

   BEGIN        {flag = 0}
   $1 == 1000   {flag = 1; 
                 next}
   flag == 1    {print; 
                 flag = 0;
                 next}

该程序在找到以 1,000 开头的行时设置一个名为“flag”的变量,然后获取下一行输入。打印下一行输入,然后清除“flag”,以便下一行不会被打印。

如果我们想打印接下来的行,我们可以使用一个名为“counter”的变量以几乎相同的方式做到这一点

   BEGIN         {counter = 0}
   $1 == 1000    {counter = 5;
                  next}
   counter > 0   {print; 
                  counter--;
                  next}

该程序在找到以 1,000 开头的行时将名为“counter”的变量初始化为 5;对于接下来的 5 行输入,它打印它们并将“counter”递减,直到它为零。

这种方法可以根据需要进行扩展。假设我们有一个输入五行的五种不同操作的列表,要在匹配一行输入后执行;然后我们可以创建一个名为“state”的变量,它存储接下来要执行的列表中的哪个项目。该方案通常如下

   BEGIN {set state to 0}
   scan the input:
      if match        set state to 1
                      get next line of input
      if state == 1   do the first thing in the list
                      state = 2
                      get next line of input
      if state == 2   do the second thing in the list
                      state = 3
                      get next line of input
      if state == 3   do the third thing in the list
                      state = 4
                      get next line of input
      if state == 4   do the fourth thing in the list
                      state = 5
                      get next line of input
      if state == 5   do the fifth (and last) thing in the list
                      state = 0
                      get next line of input

这被称为“状态机”。在这种情况下,它正在执行一个简单的操作列表,但相同的方法也可以用于执行更复杂的动作分支序列,就像我们在流程图中而不是简单的列表中可能遇到的那样。

我们可以将状态号分配给流程图中的块,然后使用 if-then 测试来设置状态变量,以指示接下来应该执行哪个备用操作。但是,很少有 Awk 程序需要如此复杂,在这里介绍更详细的示例可能会比它值得的更令人困惑。需要记住的重要一点是,awk 程序可以在一行扫描循环中在变量中给自己留言,以告诉它在以后的行扫描循环中该做什么。

华夏公益教科书