Bash-4递归的奇怪结果

时间:2017-04-05 19:21:45

标签: linux bash shell ubuntu recursion

我设计了一个简单的测试来看看Bash如何用递归行为,我不明白结果。

测试:

  1. 在shell中分配全局变量X
  2. 创建一个函数f(),用于指定同名的变量local X
  3. 分配local X全球X的值,即local X=$X
  4. 将此函数递归几次并检查每次递归时它是否使用函数上一轮的全局Xlocal X
  5. 我期待两种结果中的一种:

    • 在每次递归时,前一个local X的{​​{1}}将是新的"全球" f(),即下一个范围中的X,表示每个递归在前一个范围下创建新范围
    • 在每次递归时,前一个X值都会被遗忘,而每个新local X只会重新分配我最初分配的全局local X=$X的值。这表明Bash创建了相邻的范围。

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

    我在shell中分配了一个全局变量:

    X

    我在创建user@ubuntu-zesty:~$ X=1 的shell中创建了一个函数f(),为其分配了全局local X的值,输入了一个while循环,向X添加了1 (我假设它是本地的),打印新的local X值,并调用自身。重复5或6次。

    local X

    然后我打电话给user@ubuntu-zesty:~$ f() { local X=$X; while [ $X -lt 6 ]; do X=$(( $X + 1 )); echo $X; sleep 1; f; done; } ,输出结果令人费解。

    f()

    此时它自行退出。正如预期的那样,全球user@ubuntu-zesty:~$ 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

    那么这里发生了什么?是否有时使用全局user@ubuntu-zesty:~$ echo $X 1 ,有时使用X?如果你知道这里发生了什么,请不要给我留下血腥的细节。

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

    local X

    规格:

    • 击-4.4.5(1)-release
    • x86_64 Ubuntu Zesty
    • Linux内核4.10.0-17-通用
    • VMware Workstation 12虚拟机

4 个答案:

答案 0 :(得分:2)

bash 动态作用域,而不是静态(也称为词法)作用域。这意味着当您执行第local X=$X行时,您没有根据在全局词法作用域中指定的值获得$X的值,而是在最近的运行时作用域中存在的值,即值在f 被称为的范围内。这意味着本地值不仅在函数调用中可见,而且可以从那里进行任何调用。

请注意,这并非特定于递归。

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

答案 1 :(得分: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
...

答案 2 :(得分:1)

有31条输出线,可疑与2 5 -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; 
}

答案 3 :(得分: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