2017-04-05 69 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