首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Awk 中变量作用域的问题(2)避免变量污染的方法

Awk 中变量作用域的问题(2)避免变量污染的方法

以上两个错误的例程简单说明了在 Awk 中易导致变量污染的常见情况。那么防止这种情况的方法有哪些呢?最笨的办法,就是使函数内使用的变量与全局变量不重名。笔者很久以前使用的就是这个笨方法,对于每个函数内的“局部变量”,都以自定义的函数名称缩写作为开头,以防止在全局范围内的变量名冲突。在一定程度上,这的确是一种有效的方法。但是从逻辑上来讲,这种方法决不是万全之计。
理想的解决方案当然是在函数内定义局部变量,其实也是最容易想到的方法。在 C 语言中,函数内定义的变量自动成为局部变量,在函数结束时自动销毁。在 bash 中,虽然也有跟 Awk 类似的全局变量污染的情况,但是可以通过 local 关键字完成函数内局部变量的声明,来避免全局变量污染问题。在 Awk 中如何定义局部变量这个问题好像不是那么明显,至少在 gawk 手册的 “VARIABLES” 一节中没有关于局部变量的任何线索。非常有趣的是,虽然大部分人第一个想到是这个方案,但最终都走回了第一个方案。笔者也是在一次偶然的闲暇时光重读 sed & awk 一书时,发现有这样一段说明:“Awk 提供了一种蹩脚的方式来定义局部变量,那就是通过函数的参数列表。”不久之后,笔者又在 gawk 手册中 “USER-DEFINED FUNCTIONS” 一节中找到了相似的一段话:“由于原来的 Awk 不支持函数,局部变量在 Awk 中的实现相当笨拙,通过给函数定义额外的参数来实现。按照惯例,在真实参数后面多加几个空格,以分隔真实参数与局部变量声明。”
局部变量定义的问题解决之后,让我们回到刚才有问题的程序,以 fac1.awk 为例,我们来看修改过后的代码:
清单 3. fac1-2.awk 传统的局部变量定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1 #
2 # fac1-2.awk
3 # version 0.2 of fac1.awk
4 #
5
6 function factorial(n,    i)
7 {
8     s=1;
9
10     for (i=1; i<=n; i++)
11     {
12         s *= i;
13     }
14
15     return s;
16 }
17
18 {
19     for (i=1; i<=10; i++)
20     {
21         value = factorial(i);
22         printf("fac(%d) = %d\n", i, value);
23     }
24 }
25




运行并查看结果:
1
2
3
4
5
6
7
8
9
10
11
[robert@saphires awk_var]$ echo "" | awk -f fac1-2.awk
fac(1) = 1
fac(2) = 2
fac(3) = 6
fac(4) = 24
fac(5) = 120
fac(6) = 720
fac(7) = 5040
fac(8) = 40320
fac(9) = 362880
fac(10) = 3628800




是的,就这么简单,问题解决了!正如手册中所说,这是一个历史问题,通过形参来定义函数内局部变量只是一个折衷解决方案。对于不知道其中奥妙的人来讲,原来的函数就成了一个可变参数的函数。对于上面的例子,既可以通过 factorial(n) 调用,也可以通过 factorial(n, i) 调用,对于不明白的代码阅读者或维护者很可能就此陷入困境。因为在函数的定义中虽然有两个形参,实际上第二个是我们不希望在函数调用时引用的。
比起书上写的加空格的方法,笔者更倾向于在正常的形参后面加一个名为 _ARGVEND_ 的参数,表示正常调用所需的形参到此结束,在此标识以后的形参都是“假形参”,实际上只是局部变量的定义。天知道哪天谁看到这样的多个空格分隔参数的函数会抱怨说“这是哪个蹩脚程序员写的函数声明”然后把这几个空格去掉甚至在函数调用时引用呢。有了这样的标识,那个真正的“蹩脚程序员”至少会想一下为什么这里会有个 _ARGVEND_ 呢?当然这个标识你可以使用任何你喜欢的,但是有一点你必须注意,传统毕竟是传统,在笔者提出的方式成为传统之前,你必须知道多个空格之间的参数是怎么回事,以免让你自己成为那个真正的“蹩脚程序员”。下面是以笔者的方式修改过后的代码:
清单 4. fac1-3.awk 笔者的局部变量定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1 #
2 # fac1-3.awk
3 # version 0.3 of fac1.awk
4 #
5
6 function factorial(n, _ARGVEND_, i)
7 {
8     s=1;
9
10     for (i=1; i<=n; i++)
11     {
12         s *= i;
13     }
14
15     return s;
16 }
17
18 {
19     for (i=1; i<=10; i++)
20     {
21         value = factorial(i);
22         printf("fac(%d) = %d\n", i, value);
23     }
24 }
25




运行并查看结果:
1
2
3
4
5
6
7
8
9
10
11
[robert@saphires awk_var]$ echo "" | awk -f fac1-3.awk
fac(1) = 1
fac(2) = 2
fac(3) = 6
fac(4) = 24
fac(5) = 120
fac(6) = 720
fac(7) = 5040
fac(8) = 40320
fac(9) = 362880
fac(10) = 3628800

返回列表