admin 发表于 2021-12-23 23:23:18

golang:执行shell命令4(非阻塞,实时逐行处理标准输出)

■实例说明
我们经常需要执行本地shell命令,或者是执行其他二进制可执行文件
1)执行本地二进制可执行文件
2)非阻塞执行,不等待执行完成再继续
3)逐行实时处理标准输出内容,比如增加序号

■实例代码
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)))
                //
                // 有些系统中,会有多余多换行符,如上面注释最后部分的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
}


页: [1]
查看完整版本: golang:执行shell命令4(非阻塞,实时逐行处理标准输出)