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()
        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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM