■实例说明
我们经常需要执行本地shell命令,或者是执行其他二进制可执行文件
1)执行本地二进制可执行文件
2)非阻塞执行,不等待执行完成再继续
3)逐行实时处理标准输出内容,比如增加序号
■实例代码
[Golang] 纯文本查看 复制代码 package main
import (
"bufio"
"fmt"
"io"
"os/exec"
"strings"
)
// ExecShellNoBlock2 执行shell命令,非阻塞,即不等待执行结果
func ExecShellNoBlock2(cmd string) (string, error) {
shell := exec.Command("/bin/bash", "-c", cmd)
// 创建一个与标准输出相关联的管道
stdout, err := shell.StdoutPipe()
if err != nil {
return "", err
}
// 1、执行命令,这里不会等待命令执行完成,调用start后,命令已经开始执行了
err = shell.Start()
if err != nil {
return "", err
}
index := 0
content := make([]string, 0)
// 2、这里可以继续执行其他操作了,同时上面的shell命令也在执行中了
reader := bufio.NewReader(stdout)
for {
// 记录行数
index++
line, rErr := reader.ReadString('\n')
if rErr == io.EOF {
break
}
if rErr != nil {
// 错误传递到外面,告诉方法调用者,中间遇到了错误
err = rErr
break
}
//fmt.Println(fmt.Sprintf("%+v", []byte(line)))
// [104 101 108 108 111 32 119 111 114 108 100 10]
// 有些系统中,会有多余多换行符,如上面注释最后部分的10(ascii码中的10是换行),这里删除它
line = strings.Trim(line, string([]byte{10}))
// 打印这个内容,可以看出来是隔一秒打印一行的,即这个是实时处理返回输出的。
// 这句话只是方便看清楚实时的处理过程,无实际意义
fmt.Println(line)
// 每一行内容增加序号,我们可以用我们实际代码来替换这里
content = append(content, fmt.Sprintf("%d. %s", index, line))
}
// 这里才会等待命令执行完成
// 为什么我们要等待脚本执行完成?如果不等待,如果我们这个测试程序的main函数执行完成了,但这个脚本还没有执行完成,会被立即终止掉
// wait方法会自动关闭上面创建的管道
shell.Wait()
// 当然,我们完全可以不使用shell.Wait()等待脚本执行,完全可以像下面一样sleep 几秒钟来等待脚本执行完成。
//time.Sleep(3 * time.Second)
// 返回执行结果
return strings.Join(content, "\n"), err
}
func main() {
// 等待1秒,然后输出hello world
cmd := "sleep 1;echo hello world;sleep 1;echo hello world;sleep 1;echo hello world"
out, _ := ExecShellNoBlock2(cmd)
fmt.Println(out)
// 输出:
// hello world
// hello world
// hello world
// 1. hello world
// 2. hello world
// 3. hello world
}
|