一般狀況 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
那麼在子進程裏執行代碼有什麼意義呢?若是像上邊那樣放着前臺運行,是沒有什麼意義。但咱們能夠把它放後臺運行。bash
首先咱們先看下怎麼把單個程序放後臺運行。微信
% sleep 1000 &
[1] 850複製代碼
在 sleep 1000 後邊加一個 &,就會把它放後臺運行。而後會輸出一行內容,[1] 是進程的做業(job)號,850 是進程號(PID)。咱們能夠繼續運行別的命令,不須要等待 sleep 結束了。網絡
jobs 命令能夠查看當前在後臺運行的全部做業:socket
% jobs
[1] + running sleep 1000
# -l 會輸出進程號
% jobs -l
[1] + 850 running sleep 1000複製代碼
fg 命令能夠把後臺的做業切換回前臺:tcp
# 而後會繼續等待 sleep 運行
% fg
[1] + running sleep 1000複製代碼
若是進程已經運行起來了,咱們想再把它放到後臺,能夠這樣:ide
# 回車後按 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/zsh…
付費解決 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等領域相關問題,靈活訂價,歡迎諮詢,微信 ly50247。