在线文档 > Golang练习 > 生成进程
有时,我们的Go程序需要启动其他非Go的进程。
package main
import (
"fmt"
"io"
"os/exec"
)
func main() {
// 我们从一个简单的命令开始,它不需要任何参数或输入,只是将一些信息打印到标准输出。
// `exec.Command`函数创建了一个对象来代表该外部进程。
dateCmd := exec.Command("date")
// `Output`方法运行命令,等待其完成并收集其标准输出。
// 如果没有错误,`dateOut`将包含日期信息的字节。
dateOut, err := dateCmd.Output()
if err != nil {
panic(err)
}
fmt.Println("> date")
fmt.Println(string(dateOut))
// `Command`和`*exec.Error`和`*exec.ExitError`方法将返回`*exec.Error`,如果执行命令时出现问题(例如路径错误),
// 并且`*exec.ExitError`如果命令成功运行但退出代码为非零,则会出现此错误返回值。
_, err = exec.Command("date", "-x").Output()
if err != nil {
switch e := err.(type) {
case *exec.Error:
fmt.Println("failed executing:", err)
case *exec.ExitError:
fmt.Println("command exit rc =", e.ExitCode())
default:
panic(err)
}
}
// 接下来,我们将查看稍微复杂一些的情况,其中我们将数据从`stdin`管道传输到外部进程,并从其`stdout`中收集结果。
grepCmd := exec.Command("grep", "hello")
// 在此,我们显式地获取了输入/输出管道,启动了该进程,向其中写入一些输入,读取结果的输出,最后等待该进程退出。
grepIn, _ := grepCmd.StdinPipe()
grepOut, _ := grepCmd.StdoutPipe()
grepCmd.Start()
grepIn.Write([]byte("hello grep\ngoodbye grep"))
grepIn.Close()
grepBytes, _ := io.ReadAll(grepOut)
grepCmd.Wait()
// 我们在上面的示例中省略了错误检查,但您可以使用所有这些的通常`if err != nil`模式进行错误检查。
// 我们也只收集了`StdoutPipe`的结果,但您可以以完全相同的方式收集`StderrPipe`。
fmt.Println("> grep hello")
fmt.Println(string(grepBytes))
// 请注意,当启动命令时,我们需要提供明确定义的命令和参数数组,而不是仅仅传递一个命令行字符串。
// 如果您想要启动一个带有字符串的完整命令,可以使用`bash`的`-c`选项:
lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
lsOut, err := lsCmd.Output()
if err != nil {
panic(err)
}
fmt.Println("> ls -a -l -h")
fmt.Println(string(lsOut))
}
运行结果如下:
# 生成的程序返回的输出,和我们直接通过命令行运行这些程序的输出是相同的。
$ go run spawning-processes.go
> date
Thu 05 May 2022 10:10:12 PM PDT
# date 没有“-x”标志,因此它将退出并显示错误消息和非零返回代码。
command exited with rc = 1
> grep hello
hello grep
> ls -a -l -h
drwxr-xr-x 4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r-- 1 mark 1.3K Oct 3 16:28 spawning-processes.go