生成进程
代码实例
// 有时,我们的 Go 程序需要生成其他的,非 Go 进程。例如,这个
// 网站的语法高亮是通过在 Go 程序中生成一个 [`pygmentize`](http:///)
// 来[实现的](https://github.com/everyx/gobyexample/blob/master/tools/generate.go)。
// 让我们看一些关于 Go 生成进程的例子。
package main
import "fmt"
import "io/ioutil"
import "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))
// 下面我们将看看一个稍复杂的例子,我们将从外部进程的
// `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, _ := ioutil.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
Wed Oct 10 09:53:11 PDT 2012
> 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
执行进程
代码实例
// 在前面的例子中,我们了解了[生成外部进程](../spawning-processes/)
// 的知识,当我们需要访问外部进程时时需要这样做,但是有时候,我们只想
// 用其他的(也许是非 Go 程序)来完全替代当前的 Go 进程。这时候,我们
// 可以使用经典的 <a href="http://en./wiki/Exec_(operating_system)"><code>exec</code></a>
// 方法的 Go 实现。
package main
import "syscall"
import "os"
import "os/exec"
func main() {
// 在我们的例子中,我们将执行 `ls` 命令。Go 需要提供我
// 们需要执行的可执行文件的绝对路径,所以我们将使用
// `exec.LookPath` 来得到它(大概是 `/bin/ls`)。
binary, lookErr := exec.LookPath("ls")
if lookErr != nil {
panic(lookErr)
}
// `Exec` 需要的参数是切片的形式的(不是放在一起的一个大字
// 符串)。我们给 `ls` 一些基本的参数。注意,第一个参数需要
// 是程序名。
args := []string{"ls", "-a", "-l", "-h"}
// `Exec` 同样需要使用[环境变量](environment-variables.html)。
// 这里我们仅提供当前的环境变量。
env := os.Environ()
// 这里是 `os.Exec` 调用。如果这个调用成功,那么我们的
// 进程将在这里被替换成 `/bin/ls -a -l -h` 进程。如果存
// 在错误,那么我们将会得到一个返回值。
execErr := syscall.Exec(binary, args, env)
if execErr != nil {
panic(execErr)
}
}
运行程序
# 当我们运行程序师,它会替换为 `ls`。
$ go run execing-processes.go
total 16
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 execing-processes.go
# 注意 Go 并不提供一个经典的 Unix `fork` 函数。通常这不
# 是个问题,因为运行 Go 协程,生成进程和执行进程覆盖了
# fork 的大多数使用用场景。
信号
代码实例
// 有时候,我们希望 Go 能智能的处理 <a href="http://zh./wiki/%E4%BF%A1%E5%8F%B7_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)">Unix 信号</a>。
// 例如,我们希望当服务器接收到一个 `SIGTERM` 信号时能够
// 自动关机,或者一个命令行工具在接收到一个 `SIGINT` 信号
// 时停止处理输入信息。这里讲的就就是在 Go 中如何通过通道
// 来处理信号。
package main
import "fmt"
import "os"
import "os/signal"
import "syscall"
func main() {
// Go 通过向一个通道发送 `os.Signal` 值来进行信号通知。我们
// 将创建一个通道来接收这些通知(同时还创建一个用于在程序可
// 以结束时进行通知的通道)。
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
// `signal.Notify` 注册这个给定的通道用于接收特定信号。
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
// 这个 Go 协程执行一个阻塞的信号接收操作。当它得到一个
// 值时,它将打印这个值,然后通知程序可以退出。
go func() {
sig := <-sigs
fmt.Println()
fmt.Println(sig)
done <- true
}()
// 程序将在这里进行等待,直到它得到了期望的信号(也就
// 是上面的 Go 协程发送的 `done` 值)然后退出。
fmt.Println("awaiting signal")
<-done
fmt.Println("exiting")
}
运行程序
# 当我们运行这个程序时,它将一直等待一个信号。使用 `ctrl-C`
# (终端显示为 `^C`),我们可以发送一个 `SIGINT` 信号,这会
# 使程序打印 `interrupt` 然后退出。
$ go run signals.go
awaiting signal
^C
interrupt
exiting
退出
代码实例
// 使用 `os.Exit` 来立即进行带给定状态的退出。
package main
import "fmt"
import "os"
func main() {
// 当使用 `os.Exit` 时 `defer` 将_不会_ 执行,所以这里的 `fmt.Println`
// 将永远不会被调用。
defer fmt.Println("!")
// 退出并且退出状态为 3。
os.Exit(3)
}
// 注意,不像例如 C 语言,Go 不使用在 `main` 中返回一个整
// 数来指明退出状态。如果你想以非零状态退出,那么你就要
// 使用 `os.Exit`。
运行程序
# 如果你使用 `go run` 来运行 `exit.go`,那么退出状态将会被 `go`
# 捕获并打印。
$ go run exit.go
exit status 3
# 使用编译并执行一个二进制文件的方式,你可以在终端中查看退出状态。
$ go build exit.go
$ ./exit
$ echo $?
3
# 注意我们程序中的 `!` 永远不会被打印出来。