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

论坛 期权论坛 脚本     
已经匿名di用户   2022-5-29 19:20   798   0

最近在做一个项目的时候,需要使用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.

转载于:https://www.cnblogs.com/lizongshen/p/9375158.html

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP