iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 5
1

今日目標

昨天講了 Process 跟 Standard IO 的基本概念,今天要用他們做到下 ls 指令時真的可以跑出 ls 的結果

Shell 執行指令的流程

昨天說過任何執行中的程式都是一個 Process,當然你正在用的 Shell 也是,他負責讀取指令並且啟動新的 Process

當我下 ls 指令時,Shell 會到 /bin 裡面找到 ls 的執行檔,接著馬上啟動一個 Process 來跑 ls,一直等到 ls 跑完並輸出結果之後,Shell 才又恢復可以輸入的狀態

所以 Shell 其實就是個不斷循環的指令執行器,他不斷在 等待使用者輸入等待 Child Process 完成 之間循環,執行完一個指令後又繼續等待下個指令(跑到上圖的最右邊之後又從左邊重新開始)

實作

會用到的 function

PS. 連結可以點哦

  • exec.Command

    os/exec 是 Go 中專門在執行程式的 package,其中的 exec.Command 可以把一個指令包裝成 Cmd 型別的變數,譬如說可以用 exec.Command("ls")ls 指令包裝成一個 Cmd

  • (Cmd) Run

    Cmd 型別的變數(指令)可以用 Run 跑起來,他會在 PATH 裡面找到執行檔並且另外開一個 Child Process 來執行,所以要在 Go 裡面開一個 Child Process 來跑很簡單,就是把 exec.Command(Cmd) Run 兩個 function 組合起來而已

  • log.Println

    log 是專門用來印紀錄的 package,其中的 log.Println 可以把 log 印出來然後換行,跟普通的 Println 很像

executeInput

為了防止 main() 太複雜,我把執行指令的 function 獨立出來叫做 executeInput(),他會負責執行使用者給的指令,並且回傳過程中發生的錯誤

func executeInput(input string) error {
    // 根據使用者的輸入建立一個指令
    // 譬如說使用者輸入 ls,就建立一個 ls 指令
    cmd := exec.Command(input)
    
    // 使用 exec.Command 建立的 cmd 預設是不輸出(超怪XD)
    // 所以要把他的 Stdandard IO 重新設定成系統預設(終端機) 
    // 他才能正常輸出到終端機、從終端機讀取資料
    cmd.Stdin = os.Stdin    
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    
    // 啟動一個 Child Process 執行剛建立好的指令
    // 如果使用者輸入 ls 那就是執行 ls 的執行檔
    err := cmd.Run()
    
    // 如果有發生錯誤的話就回傳
    return err
}

首先根據使用者輸入的 input 建立指令,使用者輸入 ls 就建立 ls 的指令,接著把指令的輸入輸出位置設定成系統預設(終端機),然後就啟動一個 Child Process 來執行它

main

main() 裡面使用剛寫好的 executeInput 來執行指令,過程中若發生什麼錯誤就 log 出來

func main() {
    // ...
    for {
        fmt.Print("> ")

        input, _ := stdin.ReadString('\n')
        input = strings.TrimSpace(input)

	// 執行使用者輸入的指令
	// 如果有錯誤的話就 log 出來
        err := executeInput(input)
        if err != nil {
            log.Println(err)
        }
    }
}

Demo

執行 lsps

vim 中新增、編輯檔案

小結

做完執行指令之後,真的越來越像可以用的 Shell 了~

今天的 commit 放在 這裡,有任何問題歡迎在下方留言,沒問題的話就明天見囉


上一篇
Day04-Process & Standard IO
下一篇
Day06-執行指令(二)
系列文
Gosh!原來用 Go 寫一個 Unix Shell 這麼簡單30

1 則留言

0
一級屠豬士
iT邦大師 1 級 ‧ 2019-09-21 11:22:58

蠻不錯的.
但是直接執行 command 之前,是不是先判斷有沒有在path,會比較好一些.

https://golang.org/pkg/os/exec/#LookPath

盧承億 iT邦新手 5 級 ‧ 2019-09-21 13:52:41 檢舉

謝謝你的留言~我也有在關注你的文章XD

我原本也有想要用 LookPath 先檢查,但後來發現 (Cmd) Run 在跑之前也會檢查,如果沒有這個執行檔的話就會回傳 ErrNotFound,所以就想說給他檢查就好了

PS. 連結可以點哦

期待後續的發展,加油!

盧承億 iT邦新手 5 級 ‧ 2019-09-21 21:19:34 檢舉

你也是~一起加油!

我要留言

立即登入留言