经常用Golang中的exec.Command去调用系统命令,像这样:

func main() {
    cmd := exec.Command("showmount", "-e", "192.168.1.5")
    result, err := cmd.Output()
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println(string(result))
    }
}


来运行"showmount -e 192.168.1.5"这个命令,命令执行正常则从result中取出结果就可,但如果运行不正常呢?大部分的程序都是将错误信息打印到标准错误输出,也就是stderr,在终端中,如果执行命令出现了stderr,会将其打印在屏幕上,但其不是stdout,golang中无法从result中获取错误信息,err.Error也无法获取,因为它只返回错误代码,像这样:

[root@program ~]# showmount -e 192.168.1.5
Export list for 192.168.1.5:
/nfs  192.168.0.0/255.255.0.0
[root@program ~]# showmount -e 192.168.1.6
clnt_create: RPC: Unable to receive
[root@program ~]# go run main.go #192.168.1.6
exit status 1

这时候,老手应该会想起来用2>&1把stderr重定向stdout,比较这是编写shell脚步经常用到的。你可能会这样写:

func main() {
    cmd := exec.Command("showmount", "-e", "192.168.1.6", "2>&1')
    result, err := cmd.Output()
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println(string(result))
    }
}

但是,这样做并不可取,这样写会将‘2>&1'作为一个参数直接传给了showmount,他并不是showmount支持的参数,各种程序对输入的参数验证强度不一,轻则无法实现重定向,重则像showmount一样直接退出运行。2>&1是Shell解释器的参数,如果要用,则应该调用shell解释器运行,像这样写:

func main() {
    cmd := exec.Command("/bin/bash", "-c", "showmount -e 192.168.1.6 2>&1")
    result, err := cmd.Output()
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println(string(result))
    }
}
[root@program ~]# go run main.go #192.168.1.6
clnt_create: RPC: Unable to receive

这样一来虽然实现了想要的功能,但是还要套一层shell解释器并且多写一个2>&1,能不能在程序层面重定向stderr呢?

我尝试了cmd.Stderr 和 cmd.StderrPipe() 要么报错要么比调shell复杂,还是用shell了最终