【译】 AWK教程指南 11递归程序

  awk 中除了函数的参数列表(Argument
List)上的参数(Arguments)外,所有变量不管于何处出现,全被视为全局变量。其生命持续至程序结束——该变量不论在function外或
function内皆可使用,只要变量名称相同所使用的就是同一个变量,直到程序结束。因递归函数内部的变量,会因它调用子函数(本身)而重复使用,故编写该类函数时应特别留心。

例如:执行


    awk ‘
    BEGIN {
      x = 35
      y = 45
      test_variable( x )
      printf("Return to main : arg1= %d, x= %d, y= %d, z= %d\n", arg1, x, y, z)
    }
    function test_variable( arg1 )
    {
      arg1++ # arg1 为参数列上的参数, 是local variable. 离开此函数后将消失.
      y++   # 会改变主式中的变量 y
      z = 55 # z 为该函数中新使用的变量, 主程序中变量 z 仍可被使用.
      printf("Inside the function: arg1=%d, x=%d, y=%d, z=%d\n", arg1, x, y, z)
    } ‘

  结果屏幕打印出

        

  由上可知:

  • 函数内可任意使用主程序中的任何变量。

  • 函数内所启用的任何变量(除参数外),于该函数之外依然可以使用。

  此特性优劣参半,最大的坏处是程序中的变量不易被保护,特别是递归调用本身,执行子函数时会破坏父函数内的变量。

  一个变通的方法是:在函数的参数列中虚列一些参数。函数执行中使用这些虚列的参数来记录不想被破坏的数据,如此执行子函数时就不会破坏到这些数据。此外awk
并不会检查调用函数时所传递的参数个数是否一致。

  例如:定义递归函数如下:


    function demo( arg1 ) { # 最常见的错误例子
      ........
      for(i=1; i< 20 ; i++){
        demo(x)
        # 又调用本身. 因为 i 是 global variable, 故执行完该子函数后
        # 原函数中的 i 已经被坏, 故本函数无法正确执行.
        .......
      }
      ..........
    }

  可将上列函数中的 i 虚列在该函数的参数列上,如此 i
便是一个局部变量,不会因执行子函数而被破坏。

  将上列函数修改如下:


    function demo( arg1, i ) {
      ......
      for(i=1; i< 20; i++) {
        demo(x)  #awk不会检查呼叫函数时, 所传递的参数个数是否一致
        .....
      }
    }

  $0, $1,.., NF, NR,..也都是 global
variable,读者于递归函数中若有使用这些内置变量,也应另外设立一些局部变量来保存,以免被破坏。

范例:以下是一个常见的递归调用范例。它要求使用者输入一串元素(各元素间用空白隔开)
然后打印出这些元素所有可能的排列。

  编辑如下的awk程序,取名为 permu


    awk ‘
    BEGIN {
      print "请输入排列的元素,各元素间请用空白隔开"
      getline
      permutation($0, "")
      printf("\n共 %d 种排列方式\n", counter)
    }
    function permutation( main_lst, buffer, new_main_lst, nf, i, j )
    {
      $0 = main_lst   # 把main_lst指定给$0之后awk将自动进行字段分割.
      nf = NF       # 故可用 NF 表示 main_lst 上存在的元素个数.
      # BASE CASE : 当main_lst只有一个元素时.
      if( nf == 1){
        print buffer main_lst   #buffer的内容再加上main_lst就是完成一次排列的结果
        counter++
        return
      }
      # General Case : 每次从 main_lst 中取出一个元素放到buffer中
      # 再用 main_lst 中剩下的元素 (new_main_lst) 往下进行排列
      else for( i=1; i<=nf ;i++)
      {
        $0 = main_lst   # $0为全局变量已被破坏, 故重新把main_lst赋给$0,令awk再做一次字段分割
        new_main_lst = ""
        for(j=1; j<=nf; j++)   # 连接 new_main_lst
          if( j != i )
            new_main_lst = new_main_lst " " $j
        permutation( new_main_lst, buffer " " $i )
      }
    }
    ‘ $*

  执行    


    $ ./permu

  屏幕上出现提示信息,若输入 1 2 3 回车,结果打印出:

        

说明:

  1. 有些较旧版的awk,并不容许使用者指定$0的值。此时可改用gawk 或 nawk。否则也可自行使用 split()
函数来分割 main_lst。

  2. 为避免执行子函数时破坏 new_main_lst, nf, i, j
故把这些变量也列于参数列上。如此,new_main_lst, nf, i, j
将被当成局部变量,而不会受到子函数中同名的变量影响。读者声明函数时,参数列上不妨将这些 "虚列的参数"
与真正用于传递信息的参数间以较长的空白隔开,以便于区别。

  3. awk 中欲将字符串concatenation(连接)时,直接将两字符串并置即可(Implicit
Operator)。

  例如:


    awk ‘
    BEGIN{
      A = "This "
      B = "is a "
      C = A B "key." # 变量A与B之间应留空白,否则"AB"将代表另一新变量.
      print C
    } ‘

  结果将印出

        

  4. awk使用者所编写的函数可再重用,并不需要每个awk式中都重新编写。

  将函数部分单独编写于一文件中,当需要用到该函数时再以下列方式include进来。    


    $ awk -f 函数文件名 -f awk主程序文件名 数据文件文件名

【译】 AWK教程指南 11递归程序,布布扣,bubuko.com

时间: 2024-10-23 18:03:27

【译】 AWK教程指南 11递归程序的相关文章

【译】 AWK教程指南 6在AWK程序中使用Shell命令

awk程序中允许调用Shell指令,并提供管道解决awk与系统间数据传递的问题.所以awk很容易使用系统资源,读者可利用这个特点来编写某些适用的系统工具. 范例:写一个awk程序来打印出线上人数. 将下列程序建文件,命名为 count.awk BEGIN { while ( "who" | getline ) n++ print n } 并执行下列命令: $ awk -f count.awk 执行结果将会打印出目前在线人数. 说 明: 1. awk 程序并不一定要处理数据文件,以本例而

【译】 AWK教程指南 附录B-Actions

Actions 是由下列指令(statement)所组成: 1 表达式 ( 函数调用,赋值...) 2 print 表达式列表 3 printf( 格式化字符串, 表达式列表) 4 if( 表达式 ) 语句 [else 语句] 5 while( 表达式 ) 语句 6 do 语句 while( 表达式) 7 for( 表达式; 表达式; 表达式) 语句 8 for( variable in array) 语句 9 delete 10 break 11 continue 12 next 13 exi

【译】 AWK教程指南 7AWK应用实例

本节将示范一个统计上班到达时间及迟到次数的程序. 这程序每日被执行时将读入两个数据文件: * 员工当日到班时间的数据文件 ( 如下列的 arr.dat ) * 存放员工当月迟到累计次数的文件 当程序执行执完毕后将更新第二个数据文件的数据(迟到次数),并打印当日的报表.这程序将分成下列数小节逐步完成,其大纲如下: 7.1 在到班资料文件 arr.dat 之前增加一行抬头"ID Number Arrvial Time",并产生报表输出到文件today_rpt1 中. <在awk中如何

【译】 AWK教程指南 4通过文本内容和对比选择指定的记录

Pattern { Action }为awk中最主要的语法.若某Pattern的值为真则执行它后面的 Action. awk中常使用"关系表达式" (Relational Expression)来当成 Pattern. awk 中除了>, <, ==, != ,...等关系运算符( Relational Operators )外,另外提供 ~(match),!~(Not Match) 二个关系运算符.利用这两个运算符,可判断某字符串是否包含能匹配所指定正则表达式的子字符串.

【译】 AWK教程指南 3计算并打印文件中指定的字段数据

awk 处理数据时,它会自动从数据文件中一次读取一条记录,并会将该记录切分成一个个的字段:程序中可使用 $1, $2,... 直接取得各个字段的内容.这个特色让使用者易于用 awk 编写 reformatter 来改变数据格式. 范例:以数据文件 emp.dat 为例,计算每人应发工资并打印报表. 分析:awk 会自行一次读入一条记录,故程序中仅需告诉 awk 如何处理所读入的数据行. 执行如下命令:($ 表UNIX命令行上的提示符)  $ awk '{ print $2, $3 * $4 }'

【译】 AWK教程指南 1前言

前面的话: 这几天写了一个程序,在同一个目录里生成了很多文件,需要统计其中部分文件的总大小,发现经常用到的ls.du等命令都无济于事,我甚至都想到了最笨的方法,写一个脚本:mkdir一个新目录,把要统计总大小的文件mv过去,然后du或者ls -lh新目录.诚然,这个办法又笨又不精确,于是求助万能的网络,找到的都是同一篇用了3个很长的循环来统计的脚本,还是自己先苦读"经书"吧.鸟哥的书第十二章就有现成的示例,就用到了马上要出场的awk工具,用法如下(统计目录下所有tmp*文件的总大小,以

【译】 AWK教程指南 2概述

2.1 为什么用AWK 由于awk具有上述特色,在问题处理的过程中,可轻易使用awk来撰写一些小工具:这些小工具并非用来解决整个大问题,它们只扮演解决个别问题过程的某些角色,可通过Shell所提供的pipe将数据按需要传送给不同的小工具进行处理,以解决整个大问题.这种解题方式,使得这些小工具可因不同需求而被重复组合及使用(reuse):也可通过这种方式来先行测试大程序原型的可行性与正确性,将来若需要较高的执行速度时再用C语言来改写.这是awk最常被应用之处.若能常常如此处理问题,读者可以以更高的

【译】 AWK教程指南 附录E-正则表达式

为什么要使用正则表达式 UNIX 中提供了许多 指令 和 tools,它们具有在文件中 查找(Search)字串或替换(Replace)字串 的功能.像 grep, vi , sed, awk,... 不论是查找字串或替换字串,都得先告诉这些指令所要查找(被替换)的字串为何.若未能预先明确知道所要查找(被替换)的字串为何,只知该字串存在的范围或特征时,例如: (一)查找 "T0.c", "T1.c", "T2.c".... "T9.c&

【译】 AWK教程指南 5AWK中的数组

awk程序中允许使用字符串当做数组的下标(index).利用这个特色十分有助于资料统计工作.(使用字符串当下标的数组称为Associative Array) 首先建立一个数据文件,并取名为 reg.dat.此为一学生注册的资料文件:第一栏为学生姓名,其后为该生所修课程. awk中数组的特性 1. 使用字符串当数组的下标(index). 2. 使用数组前不须声明数组名及其大小. 例如:希望用数组来记录 reg.dat 中各门课程的修课人数.这情况,有两项信息必须储存: (a) 课程名称,如: "O