子shell、命令替換和命令組合,shell命令替換組合

來源:互聯網
上載者:User

子shell、命令替換和命令組合,shell命令替換組合

所謂子shell,即從當前shell環境新開一個shell環境,這個新開的shell環境就稱為子shell(subshell),而開啟子shell的環境稱為該子shell的父shell。子shell和父shell的關係其實就是子進程和父進程的關係,只不過子shell和父shell是關聯的進程是bash進程。

子shell會從父shell中繼承很多環境,如變數、命令全路徑、檔案描述符、當前工作目錄等等,但子shell有很多種類型,不同類型的子shell繼承的環境不相同。

可以使用$BASH_SUBSHELL變數來查看從當前進程開始的子shell層數,$BASHPID查看當前所處BASH的PID,這不同於特殊變數"$$"值,因為"$$"是從父進程繼承的。

1.1.1 何時產生子shell

嚴格地說,除了直接執行bash內建命令,在bash環境下執行的所有命令以及指令碼都會產生子shell(意味著這些命令都是在子shell環境下執行的),要解釋清楚子shell以及產生何種類型的子shell,需要搞清楚Linux中如何產生子進程。

Linux上建立子進程的方式有三種:一種是fork出來的進程,一種是exec出來的進程,一種是clone出來的進程。此處無需關係clone,因為它用來實現Linux中的線程。

(1).fork是複製進程,它會複製當前進程的副本(不考慮寫時複製的模式),以適當的方式將這些資源交給子進程。所以子進程掌握的資源和父進程是一樣的,包括記憶體中的內容,所以也包括環境變數和變數。但父子進程是完全獨立的,它們是一個程式的兩個執行個體。

(2).exec是載入另一個應用程式,替代當前啟動並執行進程,也就是說在不建立新進程的情況下載入一個新程式。exec還有一個動作:在進程執行完畢後,退出exec所在的shell環境。所以為了保證進程安全,若要形成新的且獨立的子進程,都會先fork一份當前進程,然後在fork出來的子進程上調用exec來載入新程式替代該子進程。例如在bash下執行cp命令,會先fork出一個bash,然後再exec載入cp程式覆蓋子bash進程變成cp進程。

再來說明子shell的問題。一般fork出來的子進程,內容和父進程是一樣的,包括變數,例如執行cp命令時也能擷取到父進程的變數。但是cp命令是在哪裡執行的呢?在子shell中。執行cp命令敲入斷行符號後,當前的bash進程fork出一個子bash,然後子bash通過exec載入cp程式替代子bash。

那是否可以理解為所有命令、指令碼其運行環境都是在子shell中呢?顯然,上面所說的bash內建命令不是在子shell中啟動並執行。其他的所有方式,都是在子shell中完成,只不過方式不盡相同。

分為幾種情況:

  • ①.執行bash內建命令:bash內建命令是非常特殊的,父進程不會建立子進程來執行這些命令,而是直接在當前bash進程中執行。但如果將內建命令放在管道後,則此內建命令將和管道左邊的進程同屬於一個進程組,所以仍然會建立子進程,但卻不一定是子shell。請先閱讀完下面的幾種情況再來考慮此項。
  • ②.執行bash命令本身:這是一個很巧合的命令。bash命令本身是bash內建命令,在當前shell環境下執行內建命令本不會建立子shell,也就是說不會有獨立的bash進程出現,而實際結果則表現為新的bash是一個子進程。其中一個原因是執行bash命令會載入各種環境配置項,為了父bash的環境得到保護而不被覆蓋,所以應該讓其以子shell的方式存在。雖然fork出來的bash子進程內容完全繼承父shell,但因重新載入了環境配置項,所以子shell沒有繼承普通變數,更準確的說是覆蓋了從父shell中繼承的變數。不妨試試在/etc/bashrc檔案中定義一個變數,再在父shell中export名稱相同值卻不同的環境變數,然後到子shell中看看該變數的值為何?
    • 其實執行bash命令,即可以認為是進入了子shell,也可以認為沒有進入子shell。從bash是內建命令的角度來考慮,它不會進入子shell,這一點在執行bash命令後從變數$BASH_SUBSHELL的值為0可以驗證出來。但從執行bash命令後進入了新的shell環境來看,它有其父bash進程,所以它算是進入了子shell。
  • ③.執行shell指令碼:因為指令碼中第一行總是"#!/bin/bash"或者直接"bash xyz.sh",所以這和上面的執行bash進入子shell其實是一回事,都是使用bash命令進入子shell。只不過此時的bash命令和情況②中直接執行bash命令所隱含的選項不一樣,所以繼承和載入的shell環境也不一樣。事實也確實如此,它僅只繼承父shell的某些環境變數,其餘環境一概初始化。
    • 另外,執行shell指令碼有一個動作:命令執行完畢後自動結束子shell。
  • ④.執行非bash內建命令:例如執行cp命令、grep命令等,它們直接fork一份bash進程,然後使用exec載入程式替代該子bash。此類子進程會繼承所有父bash的環境。但嚴格地說,這已經不是子shell,因為exec載入的程式已經把子bash進程替換掉了,這以為著丟失了很多bash環境。
  • ⑤.非內建命令的命令替換:當命令列中包含了命令替換部分時,將開啟一個子shell先執行這部分內容,再將執行結果返回給當前命令。因為這次的子shell不是通過bash命令進入的子shell,所以它會繼承父shell的所有變數內容。這也就解釋了"echo $(echo $$)"中"$$"的結果是當前bash的pid號,而不是子shell的pid號,但"echo $(echo $BASHPID)"卻和父bash進程的pid不同,因為它不是使用bash命令進入的子shell。
  • ⑥.使用括弧()組合一系列命令:例如(ls;date;echo haha),獨立的括弧將會開啟一個子shell來執行括弧內的命令。這種情況等同於情況⑤。

最後需要說明的是,子shell的環境設定不會粘滯到父shell環境,也就是說子shell的變數等不會影響父shell。

其實,如何判斷是否進入了子shell,方式非常簡單,執行"echo $BASHPID",如果該值和父bash進程的pid值不同,則表示進入了子shell。

例如:

[root@xuexi ~]# echo $BASHPID                # 當前bash進程的pid12904[root@xuexi ~]# echo haha | echo $BASHPID   # 非內建命令開啟的子進程組,進入了子shell13412[root@xuexi ~]# (echo $BASHPID)              # 使用括弧,進入了子shell13413[root@xuexi ~]# echo $(echo $BASHPID)       # 使用命令替換$()進入了子shell13433
1.1.2 命令替換和命令組合

Linux中使用反引號"``"(脫字元,在波浪線的按鍵上)或者$()來執行命令替換。使用括弧()來組合一系列命令。

[root@xuexi ~]# echo what date it is? $(date +%F)what date it is? 2016-09-25[root@xuexi tmp]# echo what date it is? `date +%F`  # 或者使用反引號

使用$()可以讓括弧裡的命令提前於整個命令運行,然後將執行結果插入在命令替換符號處。由於命令替換的結果經常交給外部命令,不應該讓結果有換行的行為,所以預設將所有的分行符號替換為了空格(實際上所有的空白符都被壓縮成了單個空格)。

例如:

[root@xuexi ~]# echo -e "a\nb"ab
[root@xuexi ~]# echo `echo -e "a\nb\t   \tc"`a b c

使用雙引號引用可以保留空白符。

[root@xuexi ~]# echo "`echo -e "a\nb\t   \tc"`"ab               c

從上面大概可以知道,命令替換分為兩個過程:(1)開啟子shell執行其中的命令(2)將子shell中的輸出結果打包插入在命令列中。但打包輸出結果的過程是可以控制的(例如上面使用雙引號)。

很多時候,在命令列中需要使用"cat a.txt|command"或者執行$(cat a.txt)來傳遞檔案a.txt中的內容,但這不是最好的方法。它們等價的效率更高的方法分別是"<a.txt"和"$(<a.txt)"。

如果使用括弧將一系列命令包圍,可以使得這些命令獨立於當前bash環境運行。這其實是一個命令組。

例如:

[root@xuexi ~]# (umask 077;touch new.txt;ls -l new.txt)-rw------- 1 root root 0 Aug 13 22:46 new.txt

回到系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/7401591.html註:若您覺得這篇文章還不錯請點擊下右下角的推薦,有了您的支援才能激發作者更大的寫作熱情,非常感謝!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.