手记

gocommand:一个跨平台的golang命令行执行package

最近在做一个项目的时候,需要使用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()<br>        defer outpip.Close()<br>    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()<br>                defer outpip.Close()<br>        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 testbin   dev  home  lib64  mnt  proc  run   srv  tmp  varboot  etc  lib   media  opt  root  sbin  sys  usrPASSok      gocommand   0.007s

    github开源地址:https://github.com/lizongshen/gocommand.

作者:Lizongshen  
出处:http://www.cnblogs.com/lizongshen/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。


0人推荐
随时随地看视频
慕课网APP