shell環境
每個shell進程有一個自己的運行環境,不同的Shell進程有不同的Shell環境。Shell解析命令行、調用命令行的過程都在這個環境中完成。
調用shell程序時,會讀取配置文件來初始化Shell環境。
讀取配置文件情況分為兩種:
- 用戶登錄啟動的shell
- 非用戶登錄啟動的shell
什么是子shell
所謂子shell,即從當前shell環境中新開了一個shell環境,這個新開的shell環境就是子shell,而開啟子shell的環境稱為該子shell的父shell。
子Shell的本質可以理解為Shell的子進程,子進程的概念是由父進程的概念引申而來的,在Linux系統中,系統運行的應用程序幾乎都是從init
(pid為1的進程)進程派生而來的,所有這些應用程序都可以視為init進程的子進程,而init
則為它們的父進程。通過執行pstree -a
命令就可以看到init
及系統中其他進程的進程樹信息:
[root@test ~]# pstree -a
systemd --switched-root --system --deserialize 22
├─NetworkManager --no-daemon
│ └─2*[{NetworkManager}]
├─VGAuthService -s
├─agetty --noclear tty1 linux
├─auditd
│ └─{auditd}
├─chronyd
├─crond -n
├─dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
├─irqbalance --foreground
├─lvmetad -f
├─master -w
│ ├─pickup -l -t unix -u
│ └─qmgr -l -t unix -u
├─polkitd --no-debug
│ └─6*[{polkitd}]
├─rsyslogd -n
│ └─2*[{rsyslogd}]
├─sshd -D
│ └─sshd
│ └─bash
│ └─pstree -a
├─systemd-journal
├─systemd-logind
├─systemd-udevd
├─tuned -Es /usr/sbin/tuned -l -P
│ └─4*[{tuned}]
└─vmtoolsd
└─2*[{vmtoolsd}]
Tips:若無pstree命令,請執行
yum -y install psmisc
安裝。
對於Shell的子進程來說,它是一個從父級Shell進程派生而來的新的Shell進程,我們將這種新的Shell進程稱為這個父級Shell的子Shell。
Shell腳本是從上至下、從左至右依次執行每一行的命令及語句的,即執行完一個命令之后再執行下一個。如果在Shell腳本中遇到子腳本(即腳本嵌套),就會先執行子腳本的內容,完成后再返回父腳本繼續執行父腳本內后續的命令及語句。
子shell會從父shell中繼承很多環境,如變量、命令全路徑、文件描述符、當前工作目錄、陷阱等等,但子shell有很多種類型,不同類型的子shell繼承的環境不相同。可以使用$BASH_SUBSHELL
變量來查看從當前進程開始的子shell層數,$BASHPID
查看當前所處BASH的PID,這不同於特殊變量$$
值,因為$$
在大多數情況下都會從父shell中繼承。
注意:子 Shell 雖然能使用父 Shell 的的一切,但是如果子 Shell 對數據做了修改,比如修改了全局變量,那么這種修改只能停留在子 Shell,無法傳遞給父 Shell。不管是子進程還是子 Shell,都是“傳子不傳父”。
子shell的分類
大致分為兩類:
- sub shell:通過進程替換
<(cmd),>(cmd)
、命令替換$(cmd)
、(cmd)
、|
或者$
隱式生成的子shell。因為父shell是通過fork
創建sub shell,因此子shell會從父shell中繼承很多環境,如變量、命令全路徑、文件描述符、當前工作目錄、陷阱等等; - child shell:通過以可執行文件的方式運行shell腳本或直接在當前shell中啟動shell解釋器的方式得到的子shell。父shell通過
fork-exec
的方式創建子shell,導致父shell和子shell除了維持“父子關系”外,沒有其他關聯。
注釋:使用 fork() 函數可以創建一個子進程;除了 PID(進程ID)等極少的參數不同外,子進程的一切都來自父進程,包括代碼、數據、堆棧、打開的文件等,就連代碼的執行位置(狀態)都是一樣的。