os
包及其子包 os/exec
提供了创建进程的方法。
一般的,应该优先使用 os/exec
包。因为 os/exec
包依赖 os
包中关键创建进程的 API,为了便于理解,我们先探讨 os
包中和进程相关的部分。
Go 标准库封装了更好用的包: os/exec
,运行外部命令,应该优先使用它,它包装了 os.StartProcess
函数以便更容易的重定向标准输入和输出,使用管道连接 I/O,以及作其它的一些调整。
exec.LookPath
函数在 PATH
指定目录中搜索可执行程序,如 file
中有 /
,则只在当前目录搜索。该函数返回完整路径或相对于当前路径的一个相对路径。
func LookPath(file string) (string, error)
如果在 PATH
中没有找到可执行文件,则返回 exec.ErrNotFound
。
Cmd
结构代表一个正在准备或者在执行中的外部命令,调用了 Run
、Output
或 CombinedOutput
后,Cmd
实例不能被重用。
type Cmd struct {
// Path 是将要执行的命令路径。
// 该字段不能为空(也是唯一一个不能为空的字段),如为相对路径会相对于 Dir 字段。
// 通过 Command 初始化时,会在需要时调用 LookPath 获得完整的路径。
Path string
// Args 存放着命令的参数,第一个值是要执行的命令(Args[0]);如果为空切片或者 nil,使用 {Path} 运行。
// 一般情况下,Path 和 Args 都应被 Command 函数设定。
Args []string
// Env 指定进程的环境变量,如为 nil,则使用当前进程的环境变量,即 os.Environ(),一般就是当前系统的环境变量。
Env []string
// Dir 指定命令的工作目录。如为空字符串,会在调用者的进程当前工作目录下执行。
Dir string
// Stdin 指定进程的标准输入,如为 nil,进程会从空设备读取(os.DevNull)
// 如果 Stdin 是 *os.File 的实例,进程的标准输入会直接指向这个文件
// 否则,会在一个单独的 goroutine 中从 Stdin 中读数据,然后将数据通过管道传递到该命令中(也就是从 Stdin 读到数据后,写入管道,该命令可以从管道读到这个数据)。在 goroutine 停止数据拷贝之前(停止的原因如遇到 EOF 或其他错误,或管道的 write 端错误),Wait 方法会一直堵塞。
Stdin io.Reader
// Stdout 和 Stderr 指定进程的标准输出和标准错误输出。
// 如果任一个为 nil,Run 方法会将对应的文件描述符关联到空设备(os.DevNull)
// 如果两个字段相同,同一时间最多有一个线程可以写入。
Stdout io.Writer
Stderr io.Writer
// ExtraFiles 指定额外被新进程继承的已打开文件,不包括标准输入、标准输出、标准错误输出。
// 如果本字段非 nil,其中的元素 i 会变成文件描述符 3+i。
//
// BUG: 在 OS X 10.6 系统中,子进程可能会继承不期望的文件描述符。
// http://golang.org/issue/2603
ExtraFiles []*os.File
// SysProcAttr 提供可选的、各操作系统特定的 sys 属性。
// Run 方法会将它作为 os.ProcAttr 的 Sys 字段传递给 os.StartProcess 函数。
SysProcAttr *syscall.SysProcAttr
// Process 是底层的,只执行一次的进程。
Process *os.Process
// ProcessState 包含一个已经存在的进程的信息,只有在调用 Wait 或 Run 后才可用。
ProcessState *os.ProcessState
}
一般的,应该通过 exec.Command
函数产生 Cmd
实例:
func Command(name string, arg ...string) *Cmd
该函数返回一个 *Cmd
,用于使用给出的参数执行 name
指定的程序。返回的 *Cmd
只设定了 Path
和 Args
两个字段。
如果 name
不含路径分隔符,将使用 LookPath
获取完整路径;否则直接使用 name
。参数 arg
不应包含命令名。
得到 *Cmd
实例后,接下来一般有两种写法:
func (c *Cmd) Start() error
,接着调用 func (c *Cmd) Wait() error
,然后会阻塞直到命令执行完成;func (c *Cmd) Run() error
,它内部会先调用 Start()
,接着调用 Wait()
;Output()
更是 Run()
的简便写法,外加获取外部命令的输出。
func (c *Cmd) Output() ([]byte, error)
例子:
package main
import (
"fmt"
"github.com/axgle/mahonia"
"os/exec"
)
// ConvertToString src:源字符串;srcCode:源字符串编码;tagCode:目标字符串编码
func ConvertToString(src string, srcCode string, tagCode string) string {
srcCoder := mahonia.NewDecoder(srcCode)
srcResult := srcCoder.ConvertString(src)
tagCoder := mahonia.NewDecoder(tagCode)
_, cdata, _ := tagCoder.Translate([]byte(srcResult), true)
result := string(cdata)
return result
}
func main() {
cmd := exec.Command("ipconfig")
b, err := cmd.Output()
if err != nil {
fmt.Println(err)
return
}
s := ConvertToString(string(b), "gbk", "utf8")
fmt.Println(s)
}