2017-04-05 94 views
1

我设计了一个简单的测试,看看Bash如何与递归行为,并且我不理解结果。Bash-4递归的奇怪结果

测试:

  1. 分配一个全局变量X在外壳
  2. 创建一个功能f()指派相同的名称
  3. 分配local X全球Xlocal X=$X
  4. 的值的变量 local X
  5. 重复执行此功能几次,并检查在每次递归时是否使用全局Xlocal X从功能的前转

我预期两种结局之一:

  • 在每次迭代时,local X从以前f()将成为新的“全球性” X,即X在下一个范围,这将指示每个递归创建一个新的范围在以前的范围下
  • 在每次递归时,以前的local X值将被遗忘,并且每个新的local X=$X只会重新分配我最初分配的全球X。这表明Bash会创建相邻的作用域。

我没有得到任何这些。我有些奇怪。以下是来自终端的复制粘贴。

我分配全局变量在外壳:

[email protected]:~$ X=1 

创建在创建local X壳的函数f(),分配全局X到它的值,进入一个while循环中,加1 local X(我假定它是本地的),打印新的local X值,并自行调用。重复5或6次。

[email protected]:~$ f() { local X=$X; while [ $X -lt 6 ]; do X=$(($X + 1)); echo $X; sleep 1; f; done; } 

然后我打电话f(),输出只是莫名其妙。

[email protected]:~$ f 
2 
3 
4 
5 
6 
6 
5 
6 
6 
4 
5 
6 
6 
5 
6 
6 
3 
4 
5 
6 
6 
5 
6 
6 
4 
5 
6 
6 
5 
6 
6 

在这一点上退出自身。如预期的那样,全球X未受影响。

[email protected]:~$ echo $X 
1 

那么这里发生了什么?是否有时使用全球X,有时是local X?如果你知道这里发生了什么,请不要忽略我的细节。

最后,只是为了好玩,输出的图形:

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 ====== 

规格:

  • 猛砸-4.4。5(1)-release
  • x86_64的Ubuntu的兴致
  • Linux内核4.10.0-17泛型
  • VMware Workstation的12个虚拟机

回答

2

我认为最好的方式来可视化动态作用域作为完美地@chepner在他的回答解释是稍微修改你的函数:

function f() { 
    local X="$X" 
    while [ "$X" -lt 6 ]; do 
    X=$((X + 1)) 
    echo "${FUNCNAME[*]}" "$X" # This will print the call stack 
    sleep 1 
    f 
    done 
} 

,并了解值增加:如果你按照输出列,你可以调试每个级别发生的事情。

$ f 
f f f f f 2 
f f f f f f 3 
f f f f f f f 4 
f f f f f f f f 5 
f f f f f f f f f 6 
f f f f f f f f 6 
f f f f f f f 5 
f f f f f f f f 6 
f f f f f f f 6 
f f f f f f 4 
f f f f f f f 5 
f f f f f f f f 6 
... 
1

有31条输出线,这是可疑与2 -1相同。看起来会发生的是,循环的每一次迭代都会复制该函数,其值为X与此时的值相同。

因此,在每个级别函数完成循环的剩余部分两次创建一个二叉树。视觉上最外面的部分是这样的:

4 
+--5 
| +--6 
| +--6 
+--5 
    +--6 
    +--6 

(是不是这是你的第一个建议我不能完全确定?)

这会产生相同的结果(与f 1运行),但通过该值明确地作为较低级别的参数。

f() { 
    local X=$1; 
    while [ $X -lt 6 ]; do 
    X=$(($X + 1)); 
    echo $X; 
    f $X; 
    done; 
} 
+0

很有意思,我需要后来和工作虽然这坐下。 – someguy54321

2

bash动态作用域,不是静态(又名词法)作用域。这意味着当您执行行local X=$X时,根据在全局词法作用域中分配的值,您没有得到$X的值,但是在最接近的运行时作用域中存在的值,即f的作用域中的值称为。这意味着一个本地值不仅可以在函数调用中看到,而且可以从那里调用。

请注意,这不是特定于递归。

$ X=3 
$ foo() { local X=5; bar; } 
$ bar() { echo $X; } 
$ bar 
3 
$ foo 
5 
$ echo $X 
3 
+0

好吧,这是最接近我的第一个假设,但它不明显如何解释奇数输出。我需要稍后坐下来思考@ikkachu和你的答案。 (也感谢您修复我的文章) – someguy54321

1

让我们使这个函数更加冗长,只用4作为极限。
你可以试试6.

看出口!

#!/bin/bash 
depth=0 
f() { 
    echo "depth =======$((++depth))" 
    echo "Received  $X" 
    local X=$X; 
    while ((X < 4)); do 
    ((X++)); 
    echo "Calling with $X"; 
    #sleep 1; 
    f; 
    done; 
    echo "exit with $X depth $((depth--))" 
} 

X=1 
f 
echo "final depth is $depth" 

运行,它会显示究竟发生了什么:

$ ./script 
depth =======1 
Received  1 
Calling with 2 
depth =======2 
Received  2 
Calling with 3 
depth =======3 
Received  3 
Calling with 4 
depth =======4 
Received  4 
exit with 4 depth 4 
exit with 4 depth 3 
Calling with 4 
depth =======3 
Received  4 
exit with 4 depth 3 
exit with 4 depth 2 
Calling with 3 
depth =======2 
Received  3 
Calling with 4 
depth =======3 
Received  4 
exit with 4 depth 3 
exit with 4 depth 2 
Calling with 4 
depth =======2 
Received  4 
exit with 4 depth 2 
exit with 4 depth 1 
final depth is 0