Bash 是如何從環境變數中匯入函式的

紫雲飛發表於2015-09-22

上文中曾說到:

  • 所謂的環境變數的真實面目其實就是個任意字串
  • Bash 在啟動時會將 environ 陣列中包含 = 號的字串匯入成為自己的變數
  • Bash 在啟動外部命令時會將自己內部標記為環境變數的變數重組成字串陣列賦值給 environ

本文中繼續深入講三點:

  • environ 陣列中可能存在 = 左邊名字相同的元素,也就是同名的環境變數,Bash 是怎麼匯入的?
  • Bash 還可以從環境變數中匯入函式,甚至同時匯入兩個同名的變數和函式
  • Bash 還可以同時匯出兩個同名的變數和函式

如果有兩個同名的環境變數,很簡單,那麼後面的值會覆蓋前面的:

$ env foo=1 foo=2 bash -c 'echo $foo'

2

上篇文章中我們沒有提到過函式,Bash 其實是可以從環境變數中匯入函式的,比如下面這樣:

$ foo() { echo foo函式; }

$ export -f foo

$ bash

$ foo

foo函式

上一級的 Shell 把函式傳給了它的 child shell,Bash 是怎麼實現的呢?我們用 env 命令演示一下:

$ env 'BASH_FUNC_foo%%=() { echo foo函式; }' bash -c 'foo'

foo函式

其實 Bash 就是把滿足 "BASH_FUNC_函式名%%=(){ 函式體" 格式的環境變數作為函式原始碼解析並匯入。所以兩個同名的變數和函式並不會衝突,可以同時匯入,像這樣:

$ env 'foo=1' 'BASH_FUNC_foo%%=() { echo $1; }' bash -c 'foo $foo'

1

既然可以同時匯入,那麼匯出更沒問題了:

$ foo=1

$ foo(){ echo foo函式; }

$ export foo;export -f foo

$ env

...

foo=1

BASH_FUNC_foo%%=() { echo foo函式

}

...

Bash 4.3.30 之前的版本

注意,本文所講的表現僅適用於 Bash 4.3.30 及之後的版本,之前的 Bash 版本在匯出函式時不會給函式名加上 BASH_FUNC_ 字首和 %% 字尾,在匯入時也不會識別字首字尾,只要看到 = 右邊是 "() {" 這四個字元,就按函式匯入,像這樣:

$ env 'foo=() { echo foo函式; }' bash -c 'foo'

foo函式

由於環境變數字串的轉換和識別規則不同,假如你在 Bash 4.3.30 中開啟一個 Bash 3.2.25,後者是無法繼承到前者匯出的函式的:

$ bash4.3.30

$ foo() { echo foo函式 ; }

$ export -f foo

$ bash3.2.25

$ foo

bash3.2.25: foo: command not found

反之亦然,同時 foo 會被匯入成一個變數:

$ bash3.2.25

$ foo() { echo foo函式 ; }

$ export -f foo

$ bash4.3.30

$ foo

bash3.2.25: foo: command not found

$ echo $foo

() { echo foo函式 }

相關文章