Go 语言中执行外部命令主要的方法是使用包 os/exec
。
此包的详细文档见 exec package - os/exec - pkg.go.dev,这里只介绍几种常用操作。
执行命令也分几种情况:
- 仅执行命令;
- 执行命令,获取结果,不区分 stdout 和 stderr;
- 执行命令,获取结果,区分 stdout 和 stderr。
另外,默认的命令执行是在 go 进程当前的目录下执行的,我们可能还需要指定命令执行目录。
下面我们逐个说。
1. 仅执行命令
执行命令,首先要拼接一下命令和参数,然后运行命令。
我们直接看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main
import ( "log" "os/exec" )
func ExecCommand(name string, args ...string) { cmd := exec.Command(name, args...) if err := cmd.Run(); err != nil { log.Println(err) } }
func main() { ExecCommand("ls", "-l") }
|
执行代码,没有任何输出。
上面的代码中,我们执行了命令 ls -l
,但是没有得到任何东西。
2. 获取结果
2.1. 不区分 stdout 和 stderr
要组合 stdout 和 stderr 输出,,Cmd
中有方法:
1
| func (c *Cmd) CombinedOutput() ([]byte, error)
|
用这个方法来执行命令(即这个方法是已有 Run()
方法的作用的,无需再执行 Run()
)。
我们修改上述代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main
import ( "fmt" "log" "os/exec" )
func ExecCommand(name string, args ...string) { cmd := exec.Command(name, args...)
var output []byte var err error
if output, err = cmd.CombinedOutput(); err != nil { log.Println(err) } fmt.Print(string(output)) }
func main() { ExecCommand("ls", "-l") }
|
我们得到了 ls -l
这条命令的输出:
1 2
| total 4 -rw-rw-r-- 1 gukaifeng gukaifeng 401 Jan 10 18:21 main.go
|
2.2. 区分 stdout 和 stderr
区分 stdout 和 stderr,要先给 cmd
中的成员指定一个输出 buffer,然后执行 Run()
就可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package main
import ( "bytes" "fmt" "log" "os/exec" )
func ExecCommand(name string, args ...string) { cmd := exec.Command(name, args...)
var stdout bytes.Buffer var stderr bytes.Buffer var err error
cmd.Stdout = &stdout cmd.Stderr = &stderr
if err = cmd.Run(); err != nil { log.Println(err) } fmt.Print(stdout.String()) fmt.Print(stderr.String()) }
func main() { ExecCommand("ls", "-l") }
|
3. 指定代码执行目录
指定代码执行的目录也很简单,修改 cmd.Dir
即可。
cmd.Dir
是一个 string 类型的成员,既可以是相对路径,也可以是绝对路径。
如果 cmd.Dir
是空字符串,则 Run()
在调用进程的当前目录中运行命令。
我们修改 2.2 中的代码,使其在 /
目录下执行我们的 ls -l
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package main
import ( "bytes" "fmt" "log" "os/exec" )
func ExecCommand(dir string, name string, args ...string) { cmd := exec.Command(name, args...)
var stdout bytes.Buffer var stderr bytes.Buffer var err error
cmd.Stdout = &stdout cmd.Stderr = &stderr
cmd.Dir = dir
if err = cmd.Run(); err != nil { log.Println(err) }
fmt.Print(stdout.String()) fmt.Print(stderr.String()) }
func main() { ExecCommand("/", "ls", "-l") }
|
我们修改了函数 ExecCommand()
的参数,使其在第一个参数中指定代码的执行目录。
得到以下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| total 24 lrwxrwxrwx. 1 root root 7 Nov 3 2020 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 Sep 22 10:52 boot drwxr-xr-x 19 root root 2880 Dec 7 18:05 dev drwxr-xr-x. 100 root root 8192 Dec 13 14:59 etc drwxr-xr-x. 3 root root 23 Nov 4 23:51 home lrwxrwxrwx. 1 root root 7 Nov 3 2020 lib -> usr/lib lrwxrwxrwx. 1 root root 9 Nov 3 2020 lib64 -> usr/lib64 drwxr-xr-x. 2 root root 6 Nov 3 2020 media drwxr-xr-x. 2 root root 6 Nov 3 2020 mnt drwxr-xr-x. 2 root root 6 Nov 3 2020 opt dr-xr-xr-x 134 root root 0 Dec 7 18:05 proc dr-xr-x---. 6 root root 205 Dec 13 14:59 root drwxr-xr-x 30 root root 880 Dec 7 18:05 run lrwxrwxrwx. 1 root root 8 Nov 3 2020 sbin -> usr/sbin drwxr-xr-x. 2 root root 6 Nov 3 2020 srv dr-xr-xr-x 13 root root 0 Dec 8 02:05 sys drwxrwxrwt. 10 root root 4096 Jan 10 18:58 tmp drwxr-xr-x. 12 root root 144 Sep 22 10:43 usr drwxr-xr-x. 21 root root 4096 Sep 22 02:48 var
|