【问题标题】:Odd results from Bash-4 recursionBash-4 递归的奇怪结果
【发布时间】:2017-04-05 19:21:44
【问题描述】:

我设计了一个简单的测试来查看 Bash 在递归中的表现,但我不明白结果。

测试:

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

我预期以下两种结果之一:

  • 在每次递归时,前一个f() 中的local X 将是新的“全局”X,即下一个作用域中的X,这表明每次递归都会创建一个新作用域在上一个范围之下
  • 在每次递归时,之前的local X 值将被遗忘,每个新的local X=$X 将简单地重新分配我最初分配的全局X 的值。这表明 Bash 创建了相邻的作用域。

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

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

user@ubuntu-zesty:~$ X=1

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

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?如果你知道这里发生了什么,请不要把血淋淋的细节留给我。

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

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

规格:

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

【问题讨论】:

    标签: linux bash shell ubuntu recursion


    【解决方案1】:

    我认为,正如@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】:

      bash动态 作用域的,而不是静态(又名词法)作用域的。这意味着当您执行local X=$X 行时,您不会根据在全局词法范围内分配的值获得$X 的值,而是根据最近的运行时范围内的值,即范围内的值 from其中f称为。这意味着 a 局部值不仅在函数调用中可见,而且在从那里进行的任何调用中都可见。

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

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

      【讨论】:

      • 好吧,这与我的第一个假设最接近,但它如何解释奇怪的输出并不明显。我需要稍后坐下来思考@ikkachu 和你的答案。 (也感谢您修复我的帖子)
      【解决方案3】:

      有 31 条输出线,怀疑与 25-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; 
      }
      

      【讨论】:

      • 很有趣,我需要坐下来继续工作。
      【解决方案4】:

      让函数变得更加冗长,并且只使用 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-19
        • 2012-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-17
        • 1970-01-01
        • 2017-02-21
        相关资源
        最近更新 更多