最近在做一個項目的時候,需要使用golang來調用操作系統中的命令行,來執行shell命令或者直接調用第三方程式,這其中自然就用到了golang自帶的exec.Command. 但是如果直接使用原生exec.Command會造成大量的重覆代碼,網上搜了一圈又沒有找到對exec.Command相應的封 ...
最近在做一個項目的時候,需要使用golang來調用操作系統中的命令行,來執行shell命令或者直接調用第三方程式,這其中自然就用到了golang自帶的exec.Command.
但是如果直接使用原生exec.Command會造成大量的重覆代碼,網上搜了一圈又沒有找到對exec.Command相應的封裝包,索性自己封裝了一個,取名為gocommand.目前支持Linux和Windows,歡迎各位大神在github上提交代碼補充其他平臺的實現.
下麵介紹一下gocommand庫的實現思路:
package gocommand // 命令行介面 type Commander interface { // 執行命令行並返回結果 // args: 命令行參數 // return: 進程的pid, 命令行結果, 錯誤消息 Exec(args ...string) (int, string, error) // 非同步執行命令行並通過channel返回結果 // stdout: chan結果 // args: 命令行參數 // return: 進程的pid // exception: 協程內的命令行發生錯誤時,會panic異常 ExecAsync(stdout chan string, args ...string) int // 執行命令行(忽略返回值) // args: 命令行參數 // return: 錯誤消息 ExecIgnoreResult(args ...string) error }
gocommand目前的命令行執行函數都是源於Commander介面,目前該介面定義了3個函數,分別是:執行命令行病返回結果;非同步執行命令行並得到結果;執行命令行並忽略結果.
package gocommand import ( "runtime" ) // Command的初始化函數 func NewCommand() Commander { var cmd Commander switch runtime.GOOS { case "linux": cmd = NewLinuxCommand() case "windows": cmd = NewWindowsCommand() default: cmd = NewLinuxCommand() } return cmd }
創建一個Command的實現,並根據當前的操作系統,返回對應的實現函數,目前只實現了Linux和Windows,(Mac留給各位大神(土豪)了),其中LinuxCommand的代碼實現如下:
package gocommand import ( "io/ioutil" "os" "os/exec" "syscall" ) // LinuxCommand結構體 type LinuxCommand struct { } // LinuxCommand的初始化函數 func NewLinuxCommand() *LinuxCommand { return &LinuxCommand{} } // 執行命令行並返回結果 // args: 命令行參數 // return: 進程的pid, 命令行結果, 錯誤消息 func (lc *LinuxCommand) Exec(args ...string) (int, string, error) { args = append([]string{"-c"}, args...) cmd := exec.Command(os.Getenv("SHELL"), args...) cmd.SysProcAttr = &syscall.SysProcAttr{} outpip, err := cmd.StdoutPipe()
defer outpip.Close()
if err != nil { return 0, "", err } err = cmd.Start() if err != nil { return 0, "", err } out, err := ioutil.ReadAll(outpip) if err != nil { return 0, "", err } return cmd.Process.Pid, string(out), nil } // 非同步執行命令行並通過channel返回結果 // stdout: chan結果 // args: 命令行參數 // return: 進程的pid // exception: 協程內的命令行發生錯誤時,會panic異常 func (lc *LinuxCommand) ExecAsync(stdout chan string, args ...string) int { var pidChan = make(chan int, 1) go func() { args = append([]string{"-c"}, args...) cmd := exec.Command(os.Getenv("SHELL"), args...) cmd.SysProcAttr = &syscall.SysProcAttr{} outpip, err := cmd.StdoutPipe()
defer outpip.Close()
if err != nil { panic(err) } err = cmd.Start() if err != nil { panic(err) } pidChan <- cmd.Process.Pid out, err := ioutil.ReadAll(outpip) if err != nil { panic(err) } stdout <- string(out) }() return <-pidChan } // 執行命令行(忽略返回值) // args: 命令行參數 // return: 錯誤消息 func (lc *LinuxCommand) ExecIgnoreResult(args ...string) error { args = append([]string{"-c"}, args...) cmd := exec.Command(os.Getenv("SHELL"), args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.SysProcAttr = &syscall.SysProcAttr{} err := cmd.Run() return err }
Exec函數會在執行命令行後阻塞,直到得到命令的執行結果;ExecAsync函數在內部使用了協程來執行命令行,並通過參數中的chan變數把結果傳遞出去;ExecNoWait會無阻賽地執行命令行.Windows平臺上的實現類似,只是Shell命令換成了cmd.
使用示例如下:
package main import ( "log" "github.com/lizongshen/gocommand" ) func main() { _, out, err := gocommand.NewCommand().Exec("ls /") if err != nil { log.Panic(err) } log.Println(out) }
代碼的單元測試情況:
[lizongshen@localhost gocommand]$ go test bin dev home lib64 mnt proc run srv tmp var boot etc lib media opt root sbin sys usr PASS ok gocommand 0.007s
github開源地址:https://github.com/lizongshen/gocommand.