Awk 中变量作用域的问题(1)变量污染发生的情况
- UID
- 1066743
|
Awk 中变量作用域的问题(1)变量污染发生的情况
在 C、PHP 等大多数程序语言中,函数内声明的变量都会自动成为局部变量,变量的生命周期只在函数执行期,函数返回时变量自动销毁。在 Awk 使用的大多数情况下,我们用到的都是全局变量,不曾关心过变量作用域问题。在几十上百行的小型脚本中,一线到底的脚本的确无可厚非,没有因函数调用引起的变量交叉引用,固然也不会发生全局变量污染。随着脚本规模的扩大,必将以结构化和模块化的方式来编写 Awk 脚本,自定义函数也就成了家常便饭。也许某一天增加一个小功能后发现结果不如所意,而这段代码改得如此的清晰以至于没有人会怀疑它的正确性。判断得出问题一定是历史性的,但是调试这种问题显然是既费时又费力的。笔者曾经在这个问题上花费过将近半天的时间,饱受调试痛苦之后,才注意到 Awk 中变量作用域的问题。在此把这些经验教训总结成文,以供大家参考。
变量污染发生的情况让我们通过两个例程来看看变量污染发生的情况:
清单 1. fac1.awk 打印 1 到 10 的阶乘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
26
27
28
29
30
31
32
| 1 #
2 # fac1.awk
3 # original version of fac1.awk
4 #
5
6 function factorial(n)
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
运行并查看结果:
[robert@saphires awk_var]$ echo "" | awk -f fac1.awk
fac(2) = 1
fac(4) = 6
fac(6) = 120
fac(8) = 5040
fac(10) = 362880
|
打印只显示出 2, 4, 6, 8, 10 的阶乘,而且结果还不对。很明显程序执行流程出了问题。原因在哪里呢?由于这个程序比较简单,经过简单分析可以得到全局变量 i 在自定义函数 factorial() 中被覆盖,影响了程序的工作流程,导致结果异常。
上例中 i 这样的循环变量受影响,是最典型的全局变量受污染的例子。另外,在一些递归形式的函数实现中,也很有可能出现变量污染的情况。让我们来看一看下面的例程:
清单 2. fac2.awk 打印 1 到 10 的阶乘(函数以递归方式实现)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
26
27
| 1 #
2 # fac2.awk
3 # original version of fac2.awk
4 #
5
6 function factorial(n)
7 {
8 if (n == 1)
9 {
10 i = 1;
11 return i;
12 }
13 else
14 {
15 i = factorial(n-1) * n;
16 return i;
17 }
18 }
19
20 {
21 for (i=1; i<=10; i++)
22 {
23 value = factorial(i);
24 printf("fac(%d) = %d\n", i, value);
25 }
26 }
27
|
运行并查看结果:
1
2
3
4
5
| [robert@saphires awk_var]$ echo "" | awk -f fac2.awk
fac(1) = 1
fac(2) = 2
fac(6) = 6
fac(5040) = 5040
|
结果也很奇怪。究其原因,还是全局变量 i 受污染所致。虽然上面这一段程序造得有点生硬,一般情况下不会在函数中使用 i 来处理函数的返回值,不过暴露出来的问题正在 Awk 中全局变量受污染的问题。
以上两个例程出现问题的原因是全局变量污染,但是发生全局变量污染的原因又是什么呢? gawk 用户手册中有提到,在传统的 Awk 中,是不支持函数的,程序顺序解释并执行,第一次出现的变量随即完成初始化,在以后的代码中得以引用。我们目前在 Linux 下使用的大多数是 gawk ,它是 Awk 的一个扩展实现。在原来 Awk 处理变量的原则下,gawk 引用了自定义函数,改变了原有顺序执行流程,即代码可能跳转。在没有局部变量实现的 Awk 中,变量污染就发生了。接下来我们将分析如何避免因为历史原因造成的全局变量污染问题。 |
|
|
|
|
|