文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

在Go编程中调用外部命令的几种场景

2024-11-30 02:53

关注

直接调用函数

先用Linux上的一个简单命令执行看一下效果, 执行cal命令, 会打印当前月的日期信息,如图:

如果要使用Go代码调用该命令, 可以使用以下代码:

func main(){
  cmd := exec.Command("cal")
  err := cmd.Run()
  if err != nil {
     fmt.Println(err.Error())
  }
}

首先, 调用"os/exec"包中的Command函数,并传入命令名称作为参数, Command函数会返回一个exec.Cmd的命令对象。接着调用该命令对象的Run()方法运行命令。

如果此时运行程序, 会发现什么都没有出现, 这是因为我们没有处理标准输出, 调用os/exec执行命令, 标准输出和标准错误默认会被丢弃。

这里将cmd结构中的Stdout和Stderr分别设置为os.stdout和os.Stderr, 代码如下:

func main(){
    cmd := exec.Command("cal")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()
    if err != nil {
      fmt.Println(err.Error())
    }
}

运行程序后显示:

输出到文件

输出到文件的关键, 是将exec.Cmd对象的Stdout和Stderr赋值文件句柄, 代码如下:

func main(){
    f, err := os.OpenFile("sample.txt", os.O_WRONLY|os.O_CREATE, os.ModePerm)
    if err != nil {
      fmt.Println(err.Error())
    }
    cmd := exec.Command("cal")
    cmd.Stdout = f
    cmd.Stderr = f
    err := cmd.Run()
    if err != nil {
      fmt.Println(err.Error())
    }
}

os.OpenFile打开一个文件, 指定os.0_CREATE标志让操作系统在文件不存在时自动创建, 返回文件对象*os.File, *os.File实现了io.Writer接口。

运行程序结果如下:

发送到网络

这里开启一个HTTP服务, 服务端接收两个参数:年和月, 在服务端通过执行系统命令返回结果,代码如下:

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

func queryDate(w http.ResponseWriter, r *http.Request) {
  var err error
  if r.Method == "GET" {
    year := r.URL.Query().Get("year")
    month := r.URL.Query().Get("month")

    cmd := exec.Command("cal", month, year)
    cmd.Stdout = w
    cmd.Stderr = w

    err = cmd.Run()
    if err != nil {
      fmt.Println(err.Error())
    }
  }
}

func main() {
  http.HandleFunc("/querydate", queryDate)
  http.ListenAndServe(":8001", nil)
}

打开浏览器,在地址栏中输入URL查询2023年10月份的日历:http://localhost:8001/querydate?year=2023&mnotallow=10 , 结果如下:

输出到多个目标

如果要将执行命令的结果同时输出到文件、网络和内存对象, 可以使用io.MultiWriter满足需求, io.MultiWriter可以很方便的将多个io.Writer转换成一个io.Writer, 修改之前的Web服务端程序如下:

func queryDate(w http.ResponseWriter, r *http.Request) {
  var err error
  if r.Method == "GET" {
    buffer := bytes.NewBuffer(nil)

    year := r.URL.Query().Get("year")
    month := r.URL.Query().Get("month")

    f, _ := os.OpenFile("sample.txt", os.O_WRONLY|os.O_CREATE, os.ModePerm)
    mw := io.MultiWriter(w, f, buffer)

    cmd := exec.Command("cal", month, year)
    cmd.Stdout = mw
    cmd.Stderr = mw

    err = cmd.Run()
    if err != nil {
      fmt.Println(err.Error())
    }

    fmt.Println(buffer.String())
  }
}

func main() {
  http.HandleFunc("/querydate", queryDate)
  http.ListenAndServe(":8001", nil)
}

分别获取输出内容和错误

这里我们封装一个常用函数, 输入接收命令和多个参数, 返回错误和命令返回信息, 函数代码如下:

func ExecCommandOneTimeOutput(name string, args ...string) (error, string) {
  var out bytes.Buffer
  var stderr bytes.Buffer
  cmd := exec.Command(name, args...)
  cmd.Stdout = &out
  cmd.Stderr = &stderr
  err := cmd.Run()
  if err != nil {
    fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
    return err, ""
  }
  return nil, out.String()
}

该函数可以作为通用的命令执行返回结果的函数, 分别返回了错误和命令返回信息。

循环获取命令内容

在Linux系统中,有些命令运行后结果是动态持续更新的,例如: top命令,对于该场景,我们封装函数如下:

func ExecCommandLoopTimeOutput(name string, args ...string) <-chan struct{} {
  cmd := exec.Command(name, args...)
  closed := make(chan struct{})
  defer close(closed)

  stdoutPipe, err := cmd.StdoutPipe()
  if err != nil {
    fmt.Println(err.Error())
  }
  defer stdoutPipe.Close()
  go func() {
    scanner := bufio.NewScanner(stdoutPipe)
    for scanner.Scan() {
      fmt.Println(string(scanner.Bytes()))
      _, err := simplifiedchinese.GB18030.NewDecoder().Bytes(scanner.Bytes())
      if err != nil {
        continue
      }
    }
  }()

  if err := cmd.Run(); err != nil {
    fmt.Println(err.Error())
  }
  return closed
}

通过调用cmd对象的StdoutPipe()输出管理函数, 我们可以实现持续获取后台命令返回的结果,并保持程序不退出。

在调用该函数的时候, 调用方式如下:

<-ExecCommandLoopTimeOutput("top")

打印出的信息将是一个持续显示信息,如图:

总结

本章节介绍了使用os/exec这个标准库调用外部命令的各种场景。在实际应用中, 基本用的最多的还是封装好的:ExecCommandOneTimeOutput()和ExecCommandLoopTimeOutput()两个函数, 毕竟外部命令一般只会包含两种:一种是执行后马上获取结果,第二种就是常驻内存持续获取结果。

来源:二进制空间安全内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯