一轉眼就來到第 27 天、也是實作功能的最後一天了,在過去幾個禮拜 Gosh 一直嘗試做到 zsh 能做的事(雖然還差很多XD)
但今天要來點不一樣的,我要幫 Gosh 加上一個其他 Shell 都沒有的功能:限時指令,所以不要說都在學別人的功能,Gosh 也是有他的獨特之處的啊~~
如果我希望 ping google.com
最多只跑三秒,那我就在最前面加上 timeout=3
,變成 timeout=3 ping google.com
,那他最多跑三秒之後就會自動停止
同樣,如果我希望 top
最多跑三秒,那我就可以下 timeout=3 top
,那他三秒後就會被關掉,很實用吧~
為了讓 Process 在時間到時被關掉,我們 需要在 Shell 裡面計時,時間到的時候就送一個 SIGINT 給正在跑的 Process,那 Process 就會在時間到時被殺掉
因為我們是用 Signal 來殺 Process,所以其實被執行的 Child Process 不會知道有限時,對 Child Process 來說他只是執行到一半突然被殺掉而已,所以 這個 timeout 機制可以適用於任何程式
strconv.Atoi(s string) (int, error)
Atoi
是 Ascii to Int 的意思,可以用來 把字串轉成整數,譬如說 Atoi("3")
會把字串 "3"
轉成數字 3
time.Sleep(d Duration)
time.Sleep
就跟他的名字一樣,是用來讓程式睡覺的,但他作用的範圍 只有 Goroutine(Thread) 而不是整個程式,譬如說 time.Sleep(time.Second)
可以讓目前的 Goroutine 暫停一秒再繼續跑下一行
func executeInput(input string) error {
// ...
// 預設是沒有限時的,timeout 為 0
timeout := 0
// 判斷使用者輸入的指令是不是 timeout= 開頭
if strings.HasPrefix(args[0], "timeout=") {
// 如果指令是 timeout= 開頭,那就需要限時
// 譬如說 "timeout=3 ping google.com"
// 這時 args[0] = "timeout=3"
// 那就把 args[0] 的前八個字元(timeout=)去除掉得到 "3"
// 再把字串 "3" 轉成整數,就可以成功把 timeout 設為 3
timeout, _ = strconv.Atoi(args[0][8:])
// 最後把指令中的 "timeout=3" 去掉
// 因為他並不是指令的一部分
// [timeout=3, ping, google.com] -> [ping, google.com]
args = args[1:]
}
// ...
cmd := exec.Command(args[0], args[1:]...)
// ...
// 判斷這個指令需不需要 timeout
// timeout != 0 代表有限時
if timeout != 0 {
// 新開一個 goroutine 用來計時
go func() {
// 假如需要限時、timeout=3
// 那就先讓 goroutine 睡個三秒
time.Sleep(time.Duration(timeout) * time.Second)
// 3 秒到了再發 SIGINT 給正在執行的 Process
// 這樣 Process 就會在時間到時被結束掉
cmd.Process.Signal(syscall.SIGINT)
}()
}
// ...
}
今天自己實作了一個其他 Shell 沒有的功能,感覺心裡好踏實啊,相信各位讀者也有同感吧(是嗎??)
有問題的話歡迎在下面留言,沒問題的話明天要來細數一下有哪些功能是我沒做到的,雖然沒時間做了但我會說說他們大概要怎麼做,讓大家腦中有個概念