日历

2018年八月
« 7月    
 12345
6789101112
13141516171819
20212223242526
2728293031  

最近评论

    AWK的使用

    awk的使用 第十部分 使用awk编写递归程序的实例

        awk 中除了函数的参数列(Argument List)上的参数(Arguments)外,所有变量无论在什么地方出现,均被视为全局变量。全局变量的生命周期持续到程序结束。全局变量不论在function外还是function内都可以使用,只要变量名称相同,所使用的就是同一个变量。但是递归函数会调用会调用到函数本身,所以编写这里函数是需要特别注意。
         例如:编辑一个awk脚本程序,内容如下:
    [root@myfreelinux pub]# cat argument.awk
    #!/bin/awk -f
    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程序,并查看结果如下:
    [root@myfreelinux pub]# awk -f argument.awk
    Inside the function: arg1=36,x=35,y=46,z=55
    Return to main: arg1=0,x=35,y=46,z=55
        由上以上结果可推断出:在自定义的函数内部可以随意使用主程序中的任何变量,在自定义函数内使用的变量(除参数外),在该函数外仍然可以使用。这种特性喜忧搀半,坏处是主程序中的变量不易被保护,特别是递归调用本身,执行子函数时会破坏父函数内的变量。
        一个变通的方法是:在函数的参数列中虚列一些参数。函数执行中使用这些虚列的参数来记录不想被破坏的数据,这样执行子函数时就不会破坏父函数中的参数。此外awk 并不会检查调用函数时所传递的参数个数是否一致。
        例如定义递归函数如下:
        function demo( arg1 )
         { # 最常见的错误例子
             ……………..
          for(i=1; i< 20 ; i++)
         {function(x) # 又呼叫本身。因为i 是global variable,所以执行完该子函数后原函数中的i已经被坏,所以本函数无法正确执行
            ………………}
           ………………..}
        可将上列函数中的i虚列在该函数的参数列上,这样i便是一个局部变量,不会因执行子函数而被破坏。将上列函数修改如下:
        function function( arg1,i )
        {…………….
         for(i=1; i< 20; i++)
          { function(x)#awk不会检查呼叫函数时,所传递的参数个数是否一致
          …………..} }
        $0,$1,…,NF,NR等都是awk的全局变量global variable,在递归函数中如果使用了这些awk的内部变量,可以新建一些局部变量来保存内部变量的值,以免这些内部变量的值遭到破坏。
        例如:要求输入一串数据(各数据间用空白隔开) 然后打印出这些数据的所有可能的排列。使用awk编程如下
    [root@myfreelinux pub]# cat sort.awk
    #!/bin/bash
    awk ‘
    BEGIN{
    print “please input some word,and each word separate with space:”;
    getline;
    sort($0,””);
    printf(“\n There are %d way to permutation these word\n”,counter);
    }
    function sort(all_word,buffer,new_all_word,nf,i,j)
    {
     $0=all_word; #all_word给$0之后awk将自动进行字段分割,默认使用空格分割
     nf=NF;  #分割后NF是all_word上的单词个数
       if(nf==1) #只有最后一个单词时,
     {
      print buffer all_word;#buffer的内容再加上all_word是完成一次排列的结果
      counter++;
      return;
     }
     #一般情况:每次从all_word中取出一个元素放到buffer中,再用all_word中剩下的元素new_all_word往下进行排列
     else for(i=1;i<=nf;i++)
     {
      $0=all_word;#$0作为全局变量,内容发生改变,所以重新把all_word赋给$0,令awk再做一次字段分割
      new_all_word=””;
      for(j=1;j<=nf;j++)#连接new_all_word
       if(j!=i) new_all_word=new_all_word ” ” $j;
      sort(new_all_word,buffer ” ” $i);
     }
    }’
    $*
    执行该程序,并查看运行结果:
    [root@myfreelinux pub]# bash sort.awk
    please input some word,and each word separate with space:
    my you he
     my you he
     my he you
     you my he
     you he my
     he my you
     he you my

     There are 6 way to permutation these word
        解释说明:某些旧版awk,不允许更改$0的内容,可改用gawk或nawk,也可以使用split() 函数来分割all_word。为避免执行子函数时破坏new_all_word,nf,i,j,所以将这些变量也写在参数列上。这样new_all_word,nf,i,j将被当成局部变量, 不会受到子函数中同名变量的影响。
        在声明函数时,参数列上可以将这些“虚列的参数”与真正用于传递信息的参数间以较长的空白隔开,以便于区别。awk 中要将字符串concatenation(连接)时,直接将两字符串放在一起用空格隔开即可(Implicit Operator)。 
        比如:[root@myfreelinux pub]# awk ‘BEGIN{a=”I”; b=” am”; c=a b ” a student”; print c}’
    I am a student
        需要注意c=a b “ a student”,这一句中a,b “ a student”之间要有空格将他们隔开,否则awk把他们当成一个单词了。
       awk编写的函数可以重复使用,比如把函数部分单独编写在一文件中,当需要用到这个函数时使用include,将这个函数文件包含进来,例如: $ awk -f 函数文件名 -f awk主程序文件名 数据文件名,这样,以前编写的函数程序就可以使用了,而不必重复编写这个函数了。

    评论已关闭。