天天看點

shell相關知識1

轉自https://www.cnblogs.com/ting152/p/12554480.html

1.組指令

組指令,就是将多個指令劃分為一組,或者看成一個整體。

用法 差別

Shell 組指令的寫法有兩種:

{ command1; command2;. . .;  }

(command1; command2;. . . )

  1. 由花括号

    {}

    包圍起來的組命名在目前 Shell 程序中執行,而由小括号

    ()

    包圍起來的組指令會建立一個子 Shell,所有指令都在子 Shell 中執行。
  2. 使用花括号{}時,花括号與指令之間必須要有一個空格,并且最後一個指令必須用一個分号或一個換行符結束
  3. 組指令可以将多條指令的輸出結果合并在一起,在使用重定向和管道時會特别友善。
兩種寫法的重要不同:由

{}

包圍的組指令在目前 Shell 程序中執行,由

()

包圍的組指令會建立一個子Shell,所有指令都會在這個子 Shell 中執行。

在子 Shell 中執行意味着,運作環境被複制給了一個新的 shell 程序,當這個子 Shell 退出時,新的程序也會被銷毀,環境副本也會消失,是以在子 Shell 環境中的任何更改都會消失(包括給變量指派)。是以,在大多數情況下,除非腳本要求一個子 Shell,否則使用

{}

比使用

()

更受歡迎,并且

{}

的進行速度更快,占用的記憶體更少。
舉栗
将多條指令的輸出重定向到out.txt檔案 1.普通模式
  1. ls -l > out.txt #>表示覆寫
  2. echo "test432" >> out.txt #>>表示追加
  3. cat test.txt >> out.txt

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 建立子程序

建立子程序的方式 說明
  • 第一種隻使用 fork() 函數,子程序和父程序幾乎是一模一樣的,父程序中的函數、變量(全局變量、局部變量)、檔案描述符、别名等在子程序中仍然有效。我們将這種子程序稱為子 Shell(sub shell)
  1. 使用 fork() 函數建立一個子程序相當于對父程序進行了克隆,除了 PID(程序ID)等極少的參數不同外,子程序的一切都來自父程序,包括代碼、資料、堆棧、打開的檔案等,就連代碼的執行位置(狀态)都是一樣的。
  2. 但是,後期随着各自的發展軌迹不同,兩者會變得不一樣,但是在 fork() 出來的那一刻,兩者都是一樣的。
組指令、指令替換、管道
  • 第二種使用 fork() 和 exec() 函數,即使用 fork()建立子程序後立即調用 exec() 函數加載新的可執行檔案,而不使用從父程序繼承來的一切,子程序和父程序之間除了硬生生地維持一種“父子關系”外,再也沒有任何聯系了,它們就是兩個完全不同的程式。

舉栗:

在 ~/bin 目錄下有兩個可執行檔案分别叫 a.out 和 b.out。現在運作 a.out,就會産生一個程序,比如叫做 A。在程序 A 中我又調用 fork() 函數建立了一個程序 B,那麼 B 就是 A 的子程序,此時它們是一模一樣的。但是,我調用 fork() 後立即又調用 exec() 去加載 b.out,這可就壞事了,B 程序中的一切(包括代碼、資料、堆棧等)都會被銷毀,然後再根據 b.out 重建建立一切。這樣一折騰,B 程序除了 ID 沒有變,其它的都變了,再也沒有屬于 A 的東西了。

  1. 以新程序的方式運作腳本檔案,比如

    bash ./test.sh; 

    chmod +x ./test.sh; ./test.sh

  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

exit

52886 在普通的子程序中,$ 被展開為子程序的 ID

組指令形式輸出程序ID

子shell

(echo $$;echo $PPID)

子shell和父shell中的ID是一樣的

這是因為$ 變量在子 Shell 中無效!Base 官方文檔說,在普通的子程序中,$ 确實被展開為子程序的 ID;但是在子 Shell 中,$ 卻被展開成父程序的 ID

管道形式輸出程序ID echo "test" | { echo $$;echo $PPID; }
程序替換形式輸出程序ID

read < <(echo $$ $PPID)

$ echo $REPLY

34451 34450