5

如何在Go语言中实现Unix风格的进程管道?

 9 months ago
source link: https://colobu.com/2023/10/22/how-to-use-pipe-in-Go-exec-Command/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

如何在Go语言中实现Unix风格的进程管道?

今天看到包云岗老师的一条微博:

weibo.png

这个一小时就在Unix中实现了管道的系统调用的出处来自于《Unix传奇》一书,这本书是我读过的最好的一本关于Unix历史的书籍,里面介绍了很多大神的光辉事迹,Ken Thompson是Unix的创始人之一,他还是Go语言的三巨头之一。

pipe.jpg

那么,在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:这是shell的可执行文件的名称。在大多数Unix-like系统中,sh 通常是指 Bourne Shell 或其兼容版本,例如 Bashsh 是一个命令解释器,负责执行用户提供的命令。
  • -c:这是 sh 的一个选项,表示后面会跟着一个要执行的命令字符串。

所以,当你运行 sh -c "echo Hello | grep Hello" 时,它告诉shell执行后面的命令字符串。在这个例子中,命令字符串是 "echo Hello | grep Hello",它包含了一个管道,将 echo Hello 的输出传递给 grep Hello

总结起来,sh -c 是一个在shell中执行命令字符串的机制,允许你在一个命令中组合多个子命令,包括管道和其他shell特性。

请注意,这种方法依赖于使用shell来解释命令字符串,因此可能不够安全,特别是如果输入包含用户提供的数据。确保你能够信任并控制传递给 sh -c 的字符串。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK