一、前言
在【Linux shell】中,【local】和【export】通常被拿來控制shell中變量的作用域。export被用到的場合會更多一些,local只能被用在shell函數中。
二、直接上例子
為了方便理解,嘗試寫三個簡單的shell腳本。
命令【yjcmd】:首先用shell模擬一個有輸出的,返回值為錯誤值的命令。
shell【yjshell.sh】:寫一個【local】、【export】和【默認shell變量】放在一起對比的【例子shell】。
子shell【yjchildshell.sh】:為了表達變量在shell間傳遞,寫一個【子shell】,【子shell】中嘗試訪問【yjshell.sh】中的【三種】類型的變量。
1、【yjcmd】
這個命令的作用是輸出【yjtest】,並且返回【1】,代表命令執行錯誤。
2、【yjshell.sh】
3、【yjchildshell.sh】
三、執行結果
注:【bash】和【dash】中,結果都如上圖所示,沒有差別。
四、基本規律總結
基本規律大多數常寫shell腳本的人都非常清楚,這里不過多討論,簡單總結下。
從【三、】的執行結果中可以清晰看出:
1、默認變量可以在【yjshell.sh】的任意位置訪問到,但是【不能】被【子shell】【yjchildshell.sh】繼承。
2、被【local修飾】的變量只在【當前shell函數】中生效,【yjshell.sh】的其它地方並不能得到這個變量的值,並且【子shell】也無法繼承它。
3、被【export修飾】的變量其實已經成為了【當前shell】的【環境變量】,可以被【當前shell】以及【當前shell調用的子shell】訪問到。
注:雖然export修飾的變量【作用范圍變大】,但是其僅限於【當前shell環境】和【被其調用的子shell】中,並不能影響到其【父shell】(這里例子沒有展示,簡單來說就是當前shell環境中export的內容,當前shell的父shell無法訪問)。
五、例子中的坑
我們可以看到例子中,關於函數內的打印,多了一個【$?】的輸出,【$?】是用來獲取上一條命令執行結果值的。
可以看到【yjcmd】這個命令中,命令的輸出內容是【yjtest】,命令的返回值是【1】,想要表達這個命令執行出錯了,返回了錯誤值。
通常情況下,shell腳本中的變量賦值並不會影響【$?】的數值,因為變量賦值不屬於命令范疇。
但是當命令執行的輸出被賦值給三種類型變量后,【$?】的輸出卻並不相同。
六、關於坑的探索
出現這個問題的初期,確實挺令人不解的,明明只是幾個不同作用域的變量,被【yjcmd】命令的輸出賦了值,為何會導致后續的【$?】命令取值發生了變化。
帶着這個問題探索了很長時間的百度,並沒有什么收獲。
后來回歸【local】和【export】的定義時,被“點醒”了。
【local】和【export】的定義中,對【local】和【export】的名詞定性為【命令】。
我們都知道,當描述一個物體時,通常會用形容詞+名詞的方式進行表達,形容詞通常用來說明這個【物體的性質】,名詞用來表達這個【物體的本質】。
例如面試中最為經典的【指針數組】和【數組指針】的概念區分,英文描述中借助的就是這個原理,用名詞部分區分這兩個概念的本質。
既然【local】和【export】的本質被定義為【命令】,那么這就代表【local】和【export】就具備了命令的通用特性,具備【返回值】。
所以說例子中,命令執行,命令輸出被賦值給了變量,變量被【local】和【export】修飾相當於執行了一條命令。
換句話說,被修飾的變量作用域縮小或是擴大是【local】和【export】命令作用的結果,因此【$?】的值就是【local】和【export】命令執行的返回值。
因此也就可以解釋為什么【yjcmd】明明返回的是【1】,但是【$?】的輸出確實【0】了。
七、關於坑的總結
這個小小的問題如果不被關注,可能會導致一個巨大的bug,這里記錄下來用來給自己提個醒。
需要獲取命令執行成功與否時,如果想通過返回值的方式進行判斷,那么就不要將命令的輸出【直接賦值】給【local】【export】修飾的變量。
如果必須要【直接賦值】給【local】或【export】修飾的變量,那就嘗試通過【命令輸出】判斷命令執行的結果,而不是通過【$?】來判斷。