一般狀況 zsh 腳本是在一個進程中(而且單線程)執行的,但有時咱們須要並行執行一些代碼,由於如今的 CPU 基本都是多核的,這樣能夠加快運行速度。這就涉及到進程與做業控制。這裏不講進程的概念。git
以前咱們提到過,小括號中的代碼是在子進程中執行的:github
% (sleep 1000 && echo good) # 而後再另外一個 zsh 裏查看進程 % pstree | grep sleep `-tmux: server-+-zsh---zsh---sleep
裏邊有兩個 zsh 進程。若是不加小括號的話:shell
% sleep 1000 && echo good # 而後再另外一個 zsh 裏查看進程 % pstree | grep sleep `-tmux: server-+-zsh---sleep
就只有一個 zsh 進程。這說明使用小括號時,裏邊的代碼是在子進程(一個新的 zsh 進程)執行的。但須要注意的時,若是括號裏只有一個命令(好比 sleep 1000),那麼並不會再開一個子進程來執行了。vim
那麼在子進程裏執行代碼有什麼意義呢?若是像上邊那樣放着前臺運行,是沒有什麼意義。但咱們能夠把它放後臺運行。微信
首先咱們先看下怎麼把單個程序放後臺運行。網絡
% sleep 1000 & [1] 850
在 sleep 1000 後邊加一個 &,就會把它放後臺運行。而後會輸出一行內容,[1] 是進程的做業(job)號,850 是進程號(PID)。咱們能夠繼續運行別的命令,不須要等待 sleep 結束了。socket
jobs 命令能夠查看當前在後臺運行的全部做業:tcp
% jobs [1] + running sleep 1000 # -l 會輸出進程號 % jobs -l [1] + 850 running sleep 1000
fg 命令能夠把後臺的做業切換回前臺:ide
# 而後會繼續等待 sleep 運行 % fg [1] + running sleep 1000
若是進程已經運行起來了,咱們想再把它放到後臺,能夠這樣:ui
# 回車後按 ctrl + z % sleep 1000 ^Z zsh: suspended sleep 1000 # 這時能夠運行 jobs 看一下,sleep 是處於掛起狀態的 % jobs [1] + suspended sleep 1000 # 能夠用 bg 讓 sleep 恢復運行 % bg [1] + continued sleep 1000 # 這樣 sleep 就運行在後臺了 % jobs [1] + running sleep 1000
其實 jobs、fg、bg 這些命令並不經常使用,大概瞭解下用法便可。好比如今在用 vim 編輯文件,文件尚未保存,但我想退到終端運行個命令,而後再回到 vim。能夠按 ctrl + z 讓 vim 掛起,而後運行命令,最後再運行 fg 讓 vim 恢復。但一般咱們能夠啓動多個終端模擬器,或者開一個新終端模擬器標籤,或者用 tmux,不必在一個 shell 裏這麼折騰。
那麼回答以前的場景,要在後臺進程裏執行 sleep 1000 && echo good:
% {sleep 1000 && echo aa} &
這樣大括號裏的代碼都會在後臺進程裏執行,腳本里能夠繼續寫別的。若是作完了後須要再等大括號裏邊的代碼運行。
#!/bin/zsh {sleep 5 && echo p1} & # $! 是上一個運行的後臺進程的進程號 pid=$! {sleep 10 && echo p2} & echo aaa # 要作的其餘事情先作完 sleep 2 echo bbb # wait 加進程號用來等待進程結束,相似 fg,但腳本中不能用 fg wait $pid echo ccc
結果:
% ./test.zsh aaa bbb p1 ccc # p2 是腳本運行完過幾秒才輸出的 % p2
這樣咱們就能夠同時操做多個進程來爲本身服務了。而進程之間的通訊,能夠用命名管道或者普通文件來作,也可使用 socket 文件(Zsh 中有 zsh/net/socket 模塊,使用它能夠經過 socket 文件來通訊。管道是單向的,而 socket 雙向的,更靈活一些,後續咱們會了解它的用法),或者使用網絡通訊(若是腳本分佈在不一樣的機器,zsh 中有 zsh/net/tcp 模塊,這樣無需外部命令就可進行 tcp 通訊,後續也會講到它)。
運行中的進程能夠接受信號而後對信號作出響應。kill 命令用來給進程發送信號。
15(SIGTERM)是最經常使用的信號,也是 kill 不加參數的默認信號,用於終止一個進程。kill num 便可終止進程號是 num 的進程。但 15 信號能夠被進程捕獲,而後並不退出。若是要強行殺掉一個進程,能夠用 9 信號(SIGKILL),它是進程沒法捕獲的,但這樣的話進程正在作的事情會忽然中斷,可能會有嚴重的影響,因此一般狀況不要使用 9 信號殺進程。
在腳本中捕獲信號:
#!/bin/zsh # SIGINT 是 2 信號,ctrl + c 會觸發 TRAPINT() { # 處理一些退出前的善後工做 sleep 333 } sleep 1000
而後運行這個腳本,而後 ctrl + c,腳本沒有退出,由於在執行 sleep 333,要再按一次纔會退出。
在腳本中使用信號,一般是給其餘進程發(主要是 15),而不是給本身發。在腳本中也不多須要捕獲信號處理。信號相關的更多內容,之後可能會補充。
本文大概講了進程與做業控制相關內容,主要用於在腳本里使用多進程執行代碼,而不是在終端裏進行做業控制(由於不多須要這樣作)。關於腳本中的多個進程如何配合的內容還須要繼續完善。
本文再也不更新,全系列文章在此更新維護:github.com/goreliu/zshguide
付費解決 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等領域相關問題,靈活訂價,歡迎諮詢,微信 ly50247。