1. 程式人生 > >gocommand:一個跨平臺的golang命令列執行package

gocommand:一個跨平臺的golang命令列執行package

    最近在做一個專案的時候,需要使用golang來呼叫作業系統中的命令列,來執行shell命令或者直接呼叫第三方程式,這其中自然就用到了golang自帶的exec.Command.

    但是如果直接使用原生exec.Command會造成大量的重複程式碼,網上搜了一圈又沒有找到對exec.Command相應的封裝包,索性自己封裝了一個,取名為gocommand.目前支援Linux和Windows,歡迎各位大神在github上提交程式碼補充其他平臺的實現.

    下面介紹一下gocommand庫的實現思路:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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: 錯誤訊息

ExecNoWait(args ...string) error

}

gocommand目前的命令列執行函式都是源於Commander介面,目前該介面定義了3個函式,分別是:執行命令列病返回結果;非同步執行命令列並得到結果;執行命令列並忽略結果.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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的程式碼實現如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

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

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

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

    使用示例如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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)

}

    程式碼的單元測試情況:

1

2

3

4

5

[[email protected] 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