exec包调用可执行程序

简介

go语言os/exec标准库用来调用系统的某个命令来完成一些任务。

常用方法

func LookPath(file string) (string, error)   在环境变量PATH指定的目录中搜索可执行文件,如file中有斜杠,则只在当前目录搜索。返回完整路径或者相对于当前目录的一个相对路径。
func Command(name string, arg ...string) *Cmd
方法返回一个*Cmd, 用于执行name指定的程序(携带arg参数)
func (c *Cmd) Run() error
执行Cmd中包含的命令,阻塞直到命令执行完成
func (c *Cmd) Start() error
执行Cmd中包含的命令,该方法立即返回,并不等待命令执行完成
func (c *Cmd) Wait() error
该方法会阻塞直到Cmd中的命令执行完成,但该命令必须是被Start方法开始执行的
func (c *Cmd) Output() ([]byte, error)
执行Cmd中包含的命令,并返回标准输出的切片
func (c *Cmd) CombinedOutput() ([]byte, error)
执行Cmd中包含的命令,并返回标准输出与标准错误合并后的切片
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
返回一个管道,该管道会在Cmd中的命令被启动后连接到其标准输入
func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
返回一个管道,该管道会在Cmd中的命令被启动后连接到其标准输出
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
返回一个管道,该管道会在Cmd中的命令被启动后连接到其标准错误

隐藏窗口

exec.CommandContext方法实现了context,通过context可以对exec启动的进程结束。
隐藏程序自身黑窗口的方法:Go build -ldflags="-H windows"
隐藏子进程黑窗口的方法:
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}

exec启动的进程关闭

exec.CommandContext方发实现了context,通过context可以对exec启动的进程结束。

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	cmd := exec.CommandContext(ctx, "./b")
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	cmd.Stdout = os.Stdout
	cmd.Start()

	time.Sleep(10 * time.Second)
	fmt.Println("退出程序中...", cmd.Process.Pid)
	cancel()

	cmd.Wait()
}

打印程序输出

CombinedOutput

缺点:会阻塞(我的理解是阻塞在这边一直接收输出数据,等到结束全部打印出来,使用start wait的时候要放在start前面,不然只会输出exec: already started)

	cmd = exec.Command("/bin/bash","-c",_media_bin)
	out, err = cmd.CombinedOutput()
	if err != nil {
		logs.LogInstance().Error("%s",err)
	}
	logs.LogInstance().Info(string(out))
	cmd.Run()

同步输出

项目中需要执行shell命令,虽然exec包提供了CombinedOutput()方法,在shell运行结束会返回shell执行的输出,但是用户在发起一次任务时,可能在不停的刷新log,想达到同步查看log的目的,但是CombinedOutput()方法只能在命令完全执行结束才返回整个shell的输出,所以肯定达不到效果,所以,需要寻找其它方法达到命令一边执行log一边输出的目的。

从shell执行结果的管道中获取输出

package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
)

//通过管道同步获取日志的函数
func syncLog(reader io.ReadCloser) {
	f, _ := os.OpenFile("file.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	defer f.Close()
	buf := make([]byte, 1024, 1024)
	for {
		strNum, err := reader.Read(buf)
		if strNum > 0 {
			outputByte := buf[:strNum]
			f.WriteString(string(outputByte))
		}
		if err != nil {
			break
		}
	}
}

func main() {
	//定义一个每秒1次输出的shell
	cmdStr := `
#!/bin/bash
for var in {1..10}
do
	sleep 1
     echo "Hello, Welcome ${var} times "
done`
	cmd := exec.Command("bash", "-c", cmdStr)
	//这里得到标准输出和标准错误输出的两个管道,此处获取了错误处理
	cmdStdoutPipe, _ := cmd.StdoutPipe()
	cmdStderrPipe, _ := cmd.StderrPipe()
	err := cmd.Start()
	if err != nil {
		fmt.Println(err)
	}
	go syncLog(cmdStdoutPipe)
	go syncLog(cmdStderrPipe)
	err = cmd.Wait()
	if err != nil {
		fmt.Println(err)
	}
}

指定Shell执行时的输出

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	//定义一个每秒1次输出的shell
	cmdStr := `
#!/bin/bash
for var in {1..10}
do
	sleep 1
     echo "Hello, Welcome ${var} times "
done`
	cmd := exec.Command("bash", "-c", cmdStr)
	//打开一个文件
	f, _ := os.OpenFile("file.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	defer f.Close()
	//指定输出位置
	cmd.Stderr = f
	cmd.Stdout = f
	err := cmd.Start()
	if err != nil {
		fmt.Println(err)
	}
	err = cmd.Wait()
	if err != nil {
		fmt.Println(err)
	}
}

window

隐藏窗口

运行时隐藏golang程序自己的cmd窗口
go build -ldflags -H=windowsgui 

Windows平台上,执行系统命令隐藏cmd窗口
cmd := exec.Command("sth")
if runtime.GOOS == "windows" {
    cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
}
err := cmd.Run()

实例

package main

import (
    "os"
    "os/exec"
)

func ls(path string) error {
    cmd := exec.Command("ls", path)
    cmd.Stdout = os.Stdout
    return cmd.Run()
}

func main() {
    err := ls("/")
    if err != nil {
        panic(err)
    }
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×