测试代码地址:https://github.com/kirinlabs/execshell
Golang 执行 shell 脚本,不接收返回值
// 返回一个 cmd 对象
cmd := exec.Command("sh", "-c", "./scripts/curl.sh")
// 如果只执行命令,不接收返回值
cmd.Run()
Run(),命令其实是调用了 cmd.Start()和 cmd.Wait()两个方法,只返回一个 error 对象
Golang 执行 shell脚本,接收返回值
// 返回一个 cmd 对象
cmd := exec.Command("sh", "-c", "./scripts/curl.sh")
// 收返回值[]byte, error
b,er:= cmd.Output()
log.Pringln(string(b))
返回值就是 shell 脚本打印的日志信息,但这样有一个缺点,golang 接收 shell 脚本的打印结果,必须要等待 shell 脚本全部执行完成才能一次性返回
Golang 执行 shell脚本,并实时打印 shell 脚本输出日志信息
实际业务比如:异步任务调度系统、自动发布系统等都有可能需要 shell 脚本的配合来完成,就需要实时打印 shell 脚本的中每条命令的输出日志信息,便于查看任务进度等
package main
import (
"fmt"
"io"
"log"
"os/exec"
"strings"
)
func asyncLog(reader io.ReadCloser) error {
cache := ""
buf := make([]byte, 1024, 1024)
for {
num, err := reader.Read(buf)
if err != nil {
if err == io.EOF || strings.Contains(err.Error(), "closed") {
err = nil
}
return err
}
if num > 0 {
oByte := buf[:num]
h.logInfo = append(h.logInfo, oByte...)
oSlice := strings.Split(string(oByte), "\n")
line := strings.Join(oSlice[:len(oSlice)-1], "\n")
fmt.Printf("%s%s\n", cache, line)
cache = oSlice[len(oSlice)-1]
}
}
return nil
}
func execute() error {
cmd := exec.Command("sh", "-c", "./scripts/curl.sh")
stdout, _ := cmd.StdoutPipe()
stderr, _ := cmd.StderrPipe()
if err := cmd.Start(); err != nil {
log.Printf("Error starting command: %s......", err.Error())
return err
}
go asyncLog(stdout)
go asyncLog(stderr)
if err := cmd.Wait(); err != nil {
log.Printf("Error waiting for command execution: %s......", err.Error())
return err
}
return nil
}
func main(){
execute()
}
shell脚本,每秒打印时间,观察 Golang 对日志的实时输出
#!/bin/bash
#print time
for((i=0;i<10;i++))
do
sleep 1
echo $(date +"%Y-%m-%d %H:%M:%S")
done