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

 

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

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

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

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

#!/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」。也就是說,局部變量不能被外部代碼所訪問。ui

從輸出能夠看到,在子 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 裏的一個副本。.net

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

看下面示例;繼承

  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

方法應該還有不少,這些方法的本質原理基於進程間的通訊。

相關文章
相關標籤/搜索