import "os/exec"
exec
包用于运行外部命令,它包装了os.StartProcess
,使它更容易重映射到stdin
和stdout
,或者使用管道连接I/O并进行其他调整。
与C语言或者其他编程语言的“系统”库调用不同,os/exec
包有意不调用系统shell
,也不扩展任何glob patterns
或处理一般情况下由shell
完成的其他扩展,管道或重定向。
glob patterns
是一种匹配模式,运用通配符来匹配一个文件列表。
exec
包的行为更像是C的“exec”系列函数的功能。
glob patterns
或者直接调用shell
时:path/filepath
包的Glob
函数os
包的ExpandEnv
。请注意,此包中的示例是在Unix系统中云的,可能无法在Windows上运行,并且不能在使用golang.org
和godoc.org
的Go Playground中运行。
var ErrNotFound = errors.New("executable file not found in $PATH")
ErrNotFound
是路径搜索未能找到可执行文件时导致的错误。
func LookPath(file string) (string, error)
LookPath
在PATH
环境变量命名的目录中搜索可执行文件file
。如果file
包含斜杠,则直接尝试搜索且不查询PATH
。返回结果是绝对路径或相对于当前目录的相对路径。
path, err := exec.LookPath("fortune")
if err != nil {
log.Fatal("installing fortune in your future")
}
fmt.Printf("fortune is available at %s\n", path)
type Cmd struct {
// Path是所要运行命令的路径,这是唯一必须设置为非零值的字段。
// 如果是Path是相对的,则相对于{Dir}进行评估。
Path string
// Args保存命令行参数,包括命令为Args[0]。
// 如果Args字段为空或为nil,则直接运行上述{Path}字段。
// 通常情况下,Path和Args都是通过调用Command来设置的。
Args []string
// Env指定该进程的环境变量。每个条目都是“key=value”的格式。
// 如果Env为nil,那么新创建的进程使用当前进程的环境变量。
// 在此Env切片中,如果包含的环境变量中有重复的键值,
// 那么每个重复的键值中只有最新的值会被使用。
Env []string
// Dir指定命令的工作目录。
// 如果Dir是空字符串,那么{Run}会在发起调用的进程的当前目录中运行该命令。
Dir string
// Stdin指定进程的标准输入。
// 如果Stdin为nil,则进程从null设备(os.DevNull)读取。
// 如果Stdin是*os.File,则进程的标准输入直接连接到该文件。
// 否则,在执行命令期间,单独的goroutine从Stdin读取并通过管道将数据传递给该命令。
// 在这种情况下,{Wait}将会一直等待直到goroutine停止拷贝数据,
// 即,已经读取到Stdin的末尾(EOF或读取错误),或数据写入管道时出错。
Stdin io.Reader
// Stdout和Stderr指定进程的标准输出和标准错误输出。
// 如果其中一个为nil,则{Run}将相应的文件描述符连接到空设备(os.DevNull)。
// 如果其中一个是*os.File,则进程相应的输出将直接连接到该文件。
// 否则,在命令执行期间,单独的goroutine通过管道从进程读取并将数据传递给对应的{Writer}。
// 在这种情况下,{Wait}将会一直等待直到goroutine读取到EOF或遇到错误。
// 如果Stdout和Stderr是同一个writer,并且具有可以判等(==)的类型,则一次最多只有一个goroutine调用写入函数。
Stdout io.Writer
Stderr io.Writer
// ExtraFiles指定新进程要继承的其他已打开文件。
// 它不包括标准输入,标准输出或标准错误输出。
// 如果非nil,那么条目i变为文件描述符3+i。
// Windows系统不支持ExtraFiles。
ExtraFiles []*os.File
// SysProcAttr包含可选的操作系统特定属性。
// {Run}将它作为os.ProcAttr的Sys字段传递给os.StartProcess。
SysProcAttr *syscall.SysProcAttr
// 一旦启动,Process就是底层进程。
Process *os.Process
// ProcessState包含已退出进程的信息,在一次调用后无论处于{Wait}或{Run}都可用。
ProcessState *os.ProcessState
// 包含已过滤或未导出的字段
}
Cmd表示正在准备或运行的外部命令,在调用其Run
,Output
或CombinedOutput
方法后,无法被重用。
func Command(name string, arg ...string) *Cmd
Command
返回Cmd
结构以使用给定的参数执行指定的程序,它仅在返回的Cmd结构中设置Path
和Args
。
如果name
不包含路径分隔符,则Command
使用LookPath
将name
解析为完整路径(如果可能)。否则,它直接使用name
作为Path
。
返回的Cmd结构
中Args
字段是从命令name
后紧跟的arg
元素构造而来,因此在arg
中不需要再包含命令name
。例如,Command(“echo”,“hello”)
。Args[0]
始终是name
,而不可能是已解析的路径。
在Windows
上,进程将整个命令行作为单个字符串接收并执行自己的解析。Command
使用与应用程序兼容的算法CommandLineToArgvW
(这是最常用的方式)将Args
组合并引用到命令行字符串中。值得注意的例外是msiexec.exe
和cmd.exe
(包括所有批处理文件batch files
),它们具有不同的反引用算法。在这些或其他类似情况下,可以自己进行引用并在SysProcAttr.CmdLine
中提供完整的命令行,让Args空着。
cmd := exec.Command("tr", "a-z", "A-Z")
cmd.Stdin = strings.NewReader("some input")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf("in all caps: %q\n", out.String())
cmd := exec.Command("prog")
cmd.Env = append(os.Environ(),
"FOO=duplicate_value", // 重复值将被忽略
"FOO=actual_value", // 最新值将被使用
)
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
func CommandContext(ctx context.Context, name string, arg ...string) *Cmd
CommandContext
与Command
类似,但包含上下文。
如果在命令自行完成之前context
已经结束,则提供的context
用于终止进程(通过调用os.Process.Kill
)。
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil {
// 这将在100毫秒后失败。
// 5秒的sleep进程将会被中断。
}
func (c *Cmd) CombinedOutput() ([]byte, error)
CombinedOutput
运行命令并返回标准输出和标准错误错误输出的组合。
cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", stdoutStderr)
func (c *Cmd) Output() ([]byte, error)
Output
运行命令并返回其标准输出。任何返回的错误通常都是*ExitError
类型。如果c.Stderr
为nil
,则Output
填充ExitError.Stderr
。
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
func (c *Cmd) Run() error
Run
启动指定的命令并等待它完成。
nil
。*ExitError
。对于其他情况,可能会返回其他错误类型。如果正在被调用的goroutine已使用runtime.LockOSThread
锁定操作系统线程并修改了任何可继承的OS级别线程状态(例如,Linux或Plan 9名称空间),则新进程将继承调用方的线程状态。
cmd := exec.Command("sleep", "1")
log.Printf("Running command and waiting for it to finish...")
err := cmd.Run()
log.Printf("Command finished with error: %v", err)
func (c *Cmd) Start() error
Start
启动指定的命令,但不等待它完成。
一旦命令退出,Wait
方法将返回退出状态码并释放相关资源。
cmd := exec.Command("sleep", "5")
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
log.Printf("Waiting for command to finish...")
err = cmd.Wait()
log.Printf("Command finished with error: %v", err)
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe
返回一个管道,该管道将在命令启动时连接到命令的标准错误输出。
Wait
将在看到命令退出后关闭管道,因此大多数调用者不需要自己关闭管道; 但是,需要注意的是在管道的所有读取完成之前调用Wait
是不正确的。同样的,使用StderrPipe
时使用Run
也是不正确的。
有关习惯用法,请参阅StdoutPipe示例。
cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
slurp, _ := ioutil.ReadAll(stderr)
fmt.Printf("%s\n", slurp)
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
StdinPipe
返回一个管道,该管道将在命令启动时连接到命令的标准输入。Wait
查看到命令退出后将自动关闭管道。调用者只需要调用Close
来强制管道更快关闭。例如,如果正在运行的命令在标准输入关闭之后不会退出,则调用者必须关闭管道。
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
go func() {
defer stdin.Close()
io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
}()
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
StdoutPipe
返回一个管道,该管道将在命令启动时连接到命令的标准输出。
Wait
将在看到命令退出后关闭管道,因此大多数调用者不需要自己关闭管道; 但是,值得注意的是在管道的所有读取完成之前调用Wait
是不正确的。同样的,使用StdoutPipe
时调用Run是不正确的。有关习惯用法,请参阅示例。
cmd := exec.Command("echo", "-n", `{"Name": "Bob", "Age": 32}`)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
var person struct {
Name string
Age int
}
if err := json.NewDecoder(stdout).Decode(&person); err != nil {
log.Fatal(err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d years old\n", person.Name, person.Age)
func (c *Cmd) Wait() error
Wait
等待命令退出并等待任何复制到标准输入或者从标准输出或标准错误输出复制完成。
该命令必须由Start
启动。
nil
。*ExitError
。对于I/O问题,可能会返回其他错误类型。如果c.Stdin
,c.Stdout
或c.Stderr
中的任何一个不是*os.File
,那么Wait
也会等待相应的I/O循环复制到进程或从进程中复制完成。
Wait
释放与Cmd
相关的任何资源。
type Error struct {
// Name是发生错误的文件名。
Name string
// Err是潜在的错误。
Err error
}
Error是LookPath
无法将文件归类为可执行文件时返回的。
func (e *Error) Error() string
type ExitError struct {
*os.ProcessState
// 如果没有收集标准错误输出,Stderr会保留Cmd.Output方法的标准错误输出的子集。
// 如果错误输出很长,Stderr可能只包含输出的前缀和后缀,中间替换为相关省略字节数的文本。
// 提供Stderr是用于代码调试,来包含在错误消息中。具有其他需求的用户应根据需要重定向Cmd.Stderr。
Stderr []byte
}
ExitError报告命令退出失败。
func (e *ExitError) Error() string