父 shell,子 shell ,export 與 變量傳遞

當我們在一個 shell 裏運行一個腳本程序時,該 shell 就會 fork 出一個新進程,從而啓動了另一個命令解釋器(由腳本中第一行的 #!/bin/xxx 指定,如 bash shell)來解釋運行我們這個腳本。也就是說,這個新進程是一個子 shell,而之前的 shell 是個父 shell 。

在我們所運行的腳本里,我們還可以啓動新的子 shell 進程,這些子 shell 進程使腳本並行地運行着多個子任務。一般而言,在一個腳本里執行一個外部命令(普通的可執行文件)時,shell 會 fork 出一個子進程,然後再用 exec 來執行這個程序;但是,bash shell 的內置命令(builtin)卻不會這樣,它們是直接執行的。所以,等價的內置命令的執行速度會比執行外部命令要來的快。

在一對括號 (...) 裏可以放置一組指令,這些指令是在一個子 shell 裏執行的。在子 shell 裏的變量不能被這段子 shell 外的代碼直接訪問,也就是說子 shell 裏的變量不能被父 shell 所存取,實際上它們是局部變量。這裏可以參考:Link (( ))和 [[ ]]Link shell 與 命令的執行 這兩篇文章。

下面用一段代碼進行測試:

#!/bin/bash

echo "Subshell level = $BASH_SUBSHELL"

outer_variable=Outer
outer_variable2=Outer2

(
 echo "Subshell level INSIDE subshell = $BASH_SUBSHELL"
 inner_variable=Inner
 outer_variable2=Outer_var_changein_subshell
 echo "From Subshell,\"inner_variable\"=$inner_variable"
 echo "From parent shell,\"outer\"=$outer_variable"
 echo "From parent shell, \"outer\"=$outer_variable2"
)

echo "In parent shell, check \"outer_variable\" value:$outer_variable"
echo "In parent shell, check \"outer_variable2\" value:$outer_variable2"

echo
echo "Subshell level OUTSIDE subshell = $BASH_SUBSHELL"
echo

if [ -z "$inner_variable" ]
then
    echo "inner_variable undefined in main body of shell"
else
    echo "From main body of shell,\"inner_variable\"=$inner_variable"
fi

exit 0

運行輸出:

beyes@debian:~/shell$ ./subshell.sh
Subshell level = 0
Subshell level INSIDE subshell = 1
From Subshell,"inner_variable"=Inner
From parent shell,"outer"=Outer
From parent shell, "outer"=Outer_var_changein_subshell
In parent shell, check "outer_variable" value:Outer
In parent shell, check "outer_variable2" value:Outer2
Subshell level OUTSIDE subshell = 0
inner_variable undefined in main body of shell

在上面的代碼中,BASH_SUBSHELL 是一個環境變量,它表示進入子 shell 的層級,比如處於當前 shell 時,該變量值爲 0;當在當前 shell 派生的子 shell 裏時,該變量值爲 1;如果該子 shell 又派生出一個子 shell,那麼該變量在此間的值就爲 3,以此類推。

在代碼中,( ) 裏的代碼段是在子 shell 裏執行的,而 inner_variable 作爲局部變量,它的值可以在 ( ) 這段代碼裏 echo 出來,但是一旦返回到父shell 時,它就是未定義的,所以會輸出“ inner_variable undefined in main body of shell”。也就是說,局部變量不能被外部代碼所訪問。

從輸出可以看到,在子 shell 中和父 shell 中變量 outer_variable 的輸出值是一樣的;相對應的 outer_variable2 變量即使在子 shell 中進行了修改,但是當返回到父 shell 對其輸出時,它卻還是父 shell 中原來所賦的值。從這裏可以看出,子 shell 可以 “感知” 父 shell 中的變量,但它不能修改它。其本質的原因和 fork() 函數的原理有關。在 UNIX/LINUX 中,fork 出來的子進程實際上是對父進程的一種拷貝,而子 shell 就是父shell fork 出來的一個子進程,所以它理所當然的有了父shell 中的一片拷貝。所以,子 shell 裏的 outer_variable 和 outer_variable2 變量雖然和父 shell 的同名,但它們並不是同一個變量,而是父 shell 裏的一個副本。

說到父shell 和 子 shell,那麼會想到 export 這個命令。export 也是 bash 的一個內置命令。它主要是用來將父 shell 裏的變量導出供子 shell 使用。它有如下特徵:1. 用 export 導出的變量放在“導出變量列表”中,它可以被子 shell (子 shell 的子 shell 也是如此)拷貝並使用。2. 被 export 出來的變量雖然可以被子 shell 使用,但它也只是一個拷貝,而不會影響到父 shell 中的值以及其它子 shell 中的值。

看下面示例;

  1. 先在當前 shell 裏 export 一個變量:

    beyes@debian:~/shell$ export exp8temp="hello world"
    beyes@debian:~/shell$ echo $exp8temp
    hello world
    
  2. 運行一個腳本 echo 此變量(該腳本只有一句話即 echo $exp8temp ):

    $ ./exp8.sh
    hello world
    

由上可見,父 shell 裏 export 的變量可以被子 shell 讀取。

  1. 測試一下子 shell 更改此變量是否會影響父 shell 裏的值,子 shell 代碼如下:

    #!/bin/bash
    
    exp8temp="hello shell"
    
    echo $exp8temp
    

檢驗上面的情景:

beyes@debian:~/shell$ ./exp8.sh
hello shell
beyes@debian:~/shell$ echo $exp8temp
hello world

可見子 shell 對父 shell 裏 export 出來的變量進行修改並不能影響到父 shell。這說明了,子 shell 只是在“導出變量列表“裏對該變量進行了一個拷貝。但反過來,父shell再次更改此變量時,子 shell 再去讀時,讀到的是新值,而不是原來的值。

4. 如果在子 shell 裏 export 出的變量,父 shell 是否能讀到呢?先將下面一段代碼放在後臺運行:

#!/bin/bash

export exp9temp="hello world"

sleep 30

exit 0

然後在在 30 秒內在父 shell 裏讀取一下 $exp9temp 的值,發現輸出爲空。所以我們得出結論,export 出來的變量不能導出到父進程或者是父進程的環境裏。一個自己稱可以繼承父進程的東西,而不能反過來去影響父進程。

那麼子 shell 有什麼辦法可以向父 shell 傳遞自己的變量嗎?下面方法可以考慮:

  1. 通過一箇中間文件進行:

    #!/bin/bash
    
    (
     subvar="hello shell"
     echo "$subvar" > temp.txt
    )
    
    read pvar < temp.txt
    
    echo $pvar
    
    運行輸出:
    $ sh subandp.sh
    hello shell
    
  2. 通過命令替換:

    #!/bin/bash
    
    pvar=`subvar="hello shell";echo $subvar`
    
    echo $pvar
    
運行輸出: ::
$ ./subandp.shhello shell

執行命令替換符(兩個反單引號)之間的命令也是在子 shell 來完成的。

  1. 使用命名管道:

    #!/bin/bash
    
    mkfifo -m 777 npipe
    
    (
      subsend="hello world"
      echo "$subsend" > npipe &
     )
    
    read pread < npipe
    
    echo "$pread"
    
    exit 0
    

運行輸出:

beyes@debian:~/shell$ ./var.sh
hello world

關於有名管道創建命令 mkfifo 可參考:http://www.groad.net/bbs/read.php?tid-3707.html

  1. 使用 here 文檔:

    #!/bin/bash
    
    read pvar << HERE
    `subvar="hello shell"
    echo $subvar`
    HERE
    
    echo $pvar
    

運行輸出:

$ ./subandp.sh
hello shell

方法應該還有很多,這些方法的本質原理基於進程間的通信。


轉載自:

父 shell,子 shell ,export 與 變量傳遞


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章