经常用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了最终