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

例如:执行

    awk '
    BEGIN {
      x =
      y =
      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 = # 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=; i< ; i++){
        demo(x)
        # 又调用本身. 因为 i 是 global variable, 故执行完该子函数后
        # 原函数中的 i 已经被坏, 故本函数无法正确执行.
        .......
      }
      ..........
    }

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

  将上列函数修改如下:

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

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

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

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

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

  1. 【译】 AWK教程指南

    前面的话: 这几天写了一个程序,在同一个目录里生成了很多文件,需要统计其中部分文件的总大小,发现经常用到的ls.du等命令都无济于事,我甚至都想到了最笨的方法,写一个脚本:mkdir一个新目录,把要统 ...

  2. 【译】 AWK教程指南 1前言

    前面的话: 这几天写了一个程序,在同一个目录里生成了很多文件,需要统计其中部分文件的总大小,发现经常用到的ls.du等命令都无济于事,我甚至都想到了最笨的方法,写一个脚本:mkdir一个新目录,把要统 ...

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

    本节将示范一个统计上班到达时间及迟到次数的程序. 这程序每日被执行时将读入两个数据文件: * 员工当日到班时间的数据文件 ( 如下列的 arr.dat ) * 存放员工当月迟到累计次数的文件 当程序执 ...

  4. 【译】 AWK教程指南 2概述

    2.1 为什么用AWK 由于awk具有上述特色,在问题处理的过程中,可轻易使用awk来撰写一些小工具:这些小工具并非用来解决整个大问题,它们只扮演解决个别问题过程的某些角色,可通过Shell所提供的p ...

  5. 【译】 AWK教程指南 10编写可与用户交互的AWK程序

    执行awk程序时,awk会自动从文件中读取数据来进行处理,直到文件结束.只要将awk读取数据的来源改成键盘输入,便可设计与awk 交互的程序.本节将提供一个该类程序的范例. 范例:本节将编写一个英语生 ...

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

    awk程序中允许调用Shell指令,并提供管道解决awk与系统间数据传递的问题.所以awk很容易使用系统资源,读者可利用这个特点来编写某些适用的系统工具. 范例:写一个awk程序来打印出线上人数. 将 ...

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

    为什么要使用正则表达式 UNIX 中提供了许多 指令 和 tools,它们具有在文件中 查找(Search)字串或替换(Replace)字串 的功能.像 grep, vi , sed, awk,... ...

  8. 【译】 AWK教程指南 附录D-AWK的内置变量

    因内置变量的个数不多,此处按其相关性分类说明,并未按其字母顺序排列. ARGC ARGC表示命令行上除了选项 -F, -v, -f 及其所对应的参数之外的所有参数的个数.若将"awk程序&q ...

  9. 【译】 AWK教程指南 附录C-AWK的内建函数

    C.1 字串函数 index( 原字串, 查找的子字串 ) 若原字串中含有欲寻找的子字串,则返回该子字串在原字串中第一次出现的位置,若未曾出现该子字串则返回0. 例如: $ awk 'BEGIN{ p ...

随机推荐

  1. python 读写 Excel文件

    最近用python处理一个小项目,其中涉及到对excel的读写操作,通过查资料及实践做了一下总结,以便以后用. python读写excel文件要用到两个库:xlrd和xlwt,首先下载安装这两个库. ...

  2. linux 添加用户

    1.下面就开始来说怎么添加新用户 我们以添加一个用户名为jorcen的新用户为例来说明,执行下面的命令: # useradd jorcen 2.那么就新用户就添加完成,但是没有任何信息,新用户添加完成 ...

  3. Spring MVC控制层的返回类型--String类型与Bean类型

    SpringMVC控制层的返回类型形式多样,现拿其中的两种--String类型与Bean类型作以说明. 一.测试项目的结构 说明:(jsp的名字没起好) 控制层:UserController.java ...

  4. setTimeout浅析

    刚学习javascript的时候,感觉setTimeout很好理解,不就是过n(传入的毫秒数)毫秒,执行以下传入的函数吗?这个理解伴随了我挺长的一段时间,才对setTimeout有了新的认识,请先看下 ...

  5. A JSTL primer, Part 2: Getting down to the core

    In the initial article of this series, you got your first look at JSTL. We described the use of its  ...

  6. CF 136B Ternary Logic

    http://codeforces.com/problemset/problem/136/B 题意 :就是说tor是一个三进制的运算,代表的是两个三进制数的运算,两个三进制数按位逐一相加后对三取余,没 ...

  7. linux下mysql修改数据库账户root密码

    #先停止mysql,再运行下一句 $ mysqld_safe --user=mysql --skip-grant-tables --skip-networking & $ mysql -u r ...

  8. 【转】VMware设置共享文件夹之后Ubuntu中看不到怎么办?

    一.共享文件夹设置好了,但是在虚拟机中的Ubuntu系统下却看不到,怎么办? 一种可能的原因是系统没有自动挂载,解决办法: 1.安装:               sudo apt-get insta ...

  9. Android:属性scaleType与图片的显示

    ImageView是Android中的基础图片显示控件,该控件有个重要的属性是ScaleType,该属性用以表示显示图片的方式, 共有8种取值 matrix 用矩阵来绘制(从左上角起始的矩阵区域) f ...

  10. RTDX target application does not match emulation protocol!

    2013-06-20 10:19:22 在CCS2.0 的emulator写dsp/bios 的程序,编译链接无错误,而点击LOAD Program下载xxx.out完成时弹出如下对话框: RTDX ...