如何在 Go 語言中實現 Unix 風格的進程管道?

今天看到包雲崗老師的一條微博:

這個一小時就在 Unix 中實現了管道的系統調用的出處來自於《Unix 傳奇》一書,這本書是我讀過的最好的一本關於 Unix 歷史的書籍,裏面介紹了很多大神的光輝事蹟,Ken Thompson 是 Unix 的創始人之一,他還是 Go 語言的三巨頭之一。

那麼,在 Go 語言中,如何實現進程的管道呢?

在 Go 語言中,你可以使用exec包來啓動一個進程。主要的函數是Command函數,它返回一個Cmd類型,該類型代表一個正在準備運行的命令。

以下是一個簡單的例子,演示如何啓動一個進程並執行命令:

package main

import (
 "fmt"
 "os/exec"
)

func main() {
 // 創建一個 Cmd 結構體,代表要執行的命令
 cmd := exec.Command("ls""-l")

 // 執行命令並等待完成
 err := cmd.Run()
 if err != nil {
  fmt.Println("Error executing command:", err)
  return
 }

 // 獲取命令的標準輸出
 output, err := cmd.Output()
 if err != nil {
  fmt.Println("Error getting command output:", err)
  return
 }

 // 打印輸出結果
 fmt.Println("Command Output:")
 fmt.Println(string(output))
}

在這個例子中,exec.Command("ls", "-l") 創建了一個表示運行ls -l命令的Cmd結構體。然後,cmd.Run()執行該命令,並等待它完成。最後,使用cmd.Output()獲取命令的標準輸出。 請注意,cmd.Run()會等待命令完成,而cmd.Start()可以用於啓動但不等待命令完成。你還可以使用cmd.Wait()顯式等待命令完成。

如果要實現進程的管道處理,我們可以這樣實現:

package main

import (
 "fmt"
 "os/exec"
)

func main() {
 // 創建一個 Cmd 結構體,代表第一個命令:echo Hello
 cmd1 := exec.Command("echo""Hello")

 // 創建第二個命令:grep Hello,並設置其標準輸入爲第一個命令的標準輸出
 cmd2 := exec.Command("grep""Hello")
 cmd2.Stdin, _ = cmd1.StdoutPipe()

 // 獲取第二個命令的標準輸出
 cmd2Output, _ := cmd2.Output()

 // 執行第一個命令並等待完成
 if err := cmd1.Run(); err != nil {
  fmt.Println("Error running command 1:", err)
  return
 }

 // 執行第二個命令並等待完成
 if err := cmd2.Run(); err != nil {
  fmt.Println("Error running command 2:", err)
  return
 }

 // 打印最終結果
 fmt.Println("Final Output:")
 fmt.Println(string(cmd2Output))
}

在這個例子中,cmd1 代表 echo Hello 命令,cmd2 代表 grep Hello 命令。通過將 cmd2 的標準輸入連接到 cmd1 的標準輸出,實現了管道的效果。

更簡單的,你可以使用下面的方式實現 Unix 風格管道的使用:

package main

import (
 "fmt"
 "os/exec"
)

func main() {
 // 創建一個 Cmd 結構體,代表整個命令:echo Hello | grep Hello
 cmd := exec.Command("sh""-c""echo Hello | grep Hello")

 // 獲取命令的標準輸出
 output, err := cmd.Output()
 if err != nil {
  fmt.Println("Error executing command:", err)
  return
 }

 // 打印輸出結果
 fmt.Println("Command Output:")
 fmt.Println(string(output))
}

在這個例子中,exec.Command 使用了 sh -c 來在 shell 中運行整個命令字符串 "echo Hello | grep Hello"。這樣就實現了 echo Hello | grep Hello 的效果。

sh -c 是指在 shell 中執行給定的命令字符串的選項。在這個上下文中,sh 是 shell 的可執行文件,-c 是一個選項,表示後面跟着要執行的命令字符串。

所以,當你運行 sh -c "echo Hello | grep Hello" 時,它告訴 shell 執行後面的命令字符串。在這個例子中,命令字符串是 "echo Hello | grep Hello",它包含了一個管道,將 echo Hello 的輸出傳遞給 grep Hello

總結起來,sh -c 是一個在 shell 中執行命令字符串的機制,允許你在一個命令中組合多個子命令,包括管道和其他 shell 特性。

請注意,這種方法依賴於使用 shell 來解釋命令字符串,因此可能不夠安全,特別是如果輸入包含用戶提供的數據。確保你能夠信任並控制傳遞給 sh -c 的字符串。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/x5l_UJcVw2ugEEe3zOp9Ig