- 1.組命令
- 2.子進程
- 2.1 什么是子進程
- 2.2 創建子進程
- 2.3 子進程總結
- 3.如何檢測子shell與子進程
1.組命令
組命令,就是將多個命令划分為一組,或者看成一個整體。
用法
|
區別
|
---|---|
Shell 組命令的寫法有兩種: { command1; command2;. . .; } |
兩種寫法的重要不同:由 所以在子 Shell 環境中的任何更改都會消失(包括給變量賦值)。因此,在大多數情況下,除非腳本要求一個子 Shell, 否則使用 |
舉栗 | |
將多條命令的輸出重定向到out.txt文件 | 1.普通模式
2.使用組命令 { ls -l ;echo "test432";cat test.txt; }>out.txt (ls -l ;echo "test432";cat test.txt)>out.txt |
組命令與管道結合 | (ls -l ;echo "test432";cat ../test.txt)|wc -l |
2.子進程
2.1 什么是子進程
子進程的概念是由父進程的概念引申而來的。在 Linux 系統中,系統運行的應用程序幾乎都是從 init(pid為 1 的進程)進程派生而來的,所有這些應用程序都可以視為 init 進程的子進程,而 init 則為它們的父進程。
Shell 腳本是從上至下、從左至右依次執行的,即執行完一個命令之后再執行下一個。如果在 Shell 腳本中遇到子腳本(即腳本嵌套,但是必須以新進程的方式運行)或者外部命令,就會向系統內核申請創建一個新的進程,以便在該進程中執行子腳本或者外部命令,這個新的進程就是子進程。子進程執行完畢后才能回到父進程,才能繼續執行父腳本中后續的命令及語句。
使用pstree -p
命令就可以看到 init 及系統中其他進程的進程樹信息(包括 pid):
systemd(1)─┬─ModemManager(796)─┬─{ModemManager}(821) │ └─{ModemManager}(882) ├─NetworkManager(975)─┬─{NetworkManager}(1061) │ └─{NetworkManager}(1077) ├─abrt-watch-log(774) ├─abrt-watch-log(776) ├─abrtd(773) ├─accounts-daemon(806)─┬─{accounts-daemon}(839) │ └─{accounts-daemon}(883) ├─alsactl(768) ├─at-spi-bus-laun(1954)─┬─dbus-daemon(1958)───{dbus-daemon}(1960) │ ├─{at-spi-bus-laun}(1955) │ ├─{at-spi-bus-laun}(1957) │ └─{at-spi-bus-laun}(1959) ├─at-spi2-registr(1962)───{at-spi2-registr}(1965) ├─atd(842) ├─auditd(739)─┬─audispd(753)─┬─sedispatch(757) │ │ └─{audispd}(759) │ └─{auditd}(752)
2.2 創建子進程
創建子進程的方式
|
說明
|
|
---|---|---|
|
|
組命令、命令替換、管道 |
|
舉栗: |
1.以新進程的方式運行腳本文件,比如 2.在當前 Shell 中使用 bash 命令啟動新的 Shell |
2.3 子進程總結
子 Shell 雖然能使用父 Shell 的的一切,但是如果子 Shell 對數據做了修改,比如修改了全局變量,這種修改也只能停留在子 Shell,無法傳遞給父 Shell。不管是子進程還是子 Shell,都是“傳子不傳父”。
子 Shell 才是真正繼承了父進程的一切,這才像“一個模子刻出來的”;普通子進程和父進程是完全不同的兩個程序,只是維持着父子關系而已。
3.如何檢測子shell與子進程
echo $$輸出當前進程ID,echo $PPID輸出父shell ID
命令 |
結果 |
結論 |
|
---|---|---|---|
輸出當前進程與父進程ID |
echo $$;echo $PPID |
34451 34450 |
|
子進程形式輸出進程ID 子進程 |
bash echo $$;echo $PPID exit |
52886 34451 |
在普通的子進程中,$ 被展開為子進程的 ID |
組命令形式輸出進程ID 子shell |
(echo $$;echo $PPID)
|
34451 34450 |
子shell和父shell中的ID是一樣的 這是因為$ 變量在子 Shell 中無效!Base 官方文檔說,在普通的子進程中,$ 確實被展開為子進程的 ID; 但是在子 Shell 中,$ 卻被展開成父進程的 ID |
管道形式輸出進程ID 子shell |
echo "test" | { echo $$;echo $PPID; } |
34451 34450 |
|
進程替換形式輸出進程ID |
read < <(echo $$ $PPID) $ echo $REPLY |
34451 34450 |
除了 $,Bash 還提供了另外兩個環境變量——SHLVL 和 BASH_SUBSHELL,用它們來檢測子 Shell 非常方便。
SHLVL 是記錄多個 Bash 進程實例嵌套深度的累加器,每次進入一層普通的子進程,SHLVL 的值就加 1。而 BASH_SUBSHELL 是記錄一個 Bash 進程實例中多個子 Shell(sub shell)嵌套深度的累加器,每次進入一層子 Shell,BASH_SUBSHELL 的值就加 1。
命令 | 結果 | 知識點 | |
---|---|---|---|
輸出變量 | echo "$SHLVL $BASH_SUBSHELL" |
1 0 |
|
子進程形式輸出變量 子進程 |
創建一個腳本文件,命名為 test.sh,內容如下: #!/bin/bash echo "$SHLVL $BASH_SUBSHELL" ***************************************** bash echo "$SHLVL $BASH_SUBSHELL"#2 0 bash ./test.sh #3 0 echo "$SHLVL $BASH_SUBSHELL"#2 0 chmod +x ./test.sh;./test.sh #3 0 echo "$SHLVL $BASH_SUBSHELL"#2 0 exit #退出內層Shell echo "$SHLVL $BASH_SUBSHELL"#1 0 |
這兩種運行腳本的方式,在腳本運行期間會開啟一個子進程, 運行結束后立即退出子進程 產生新進程時,SHLVL的值加1 |
|
組命令形式輸出變量 子shell |
(echo "$SHLVL $BASH_SUBSHELL") |
1 1 | 組命令、管道、命令替換這幾種方式都會產生子 Shell |
管道形式輸出變量 子shell |
echo "test" | { echo "$SHLVL $BASH_SUBSHELL"; } |
1 1 | |
命令替換形式輸出變量 子shell |
var=$(echo "$SHLVL $BASH_SUBSHELL") echo $var |
1 1 | |
四層組命令形式輸出變量 子shell |
( ( ( (echo "$SHLVL $BASH_SUBSHELL") ) ) ) |
1 4 | |
進程替換形式輸出變量
|
read < <(echo "$SHLVL $BASH_SUBSHELL") echo $REPLY
echo "hello" > >(echo "$SHLVL $BASH_SUBSHELL")
|
1 0 1 0 |
進程替換只是借助文件在()內部和外部命令之間傳遞數據, 並沒有創建子shell, (也就是當前進程)中執行的
|