【go语言】调用执行外部命令(程序)的方法
专栏:web开发笔记 April 22, 2025, 5:51 p.m. 33 阅读
在go里可以方便的调用和执行外部程序,并捕获执行输出。也可以利用goroutine来方便地并行执行程序。

1. 调用外部程序

在go中用os/exec来调用和执行外部命令,如下,先通过exec.Command来构建cmd结构体,再cmd.Run()

package main
import (
    "os/exec"
)

func main() {
    cmd := exec.Command("./test")
    cmd.Run()
}

2. Terminal里实时输出信息

但是这样调用Terminal里并不会显示./test程序的输出。我们需要指定一下标准输入输出。

    cmd := exec.Command("./test")
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()

3. 简单获取执行结果

如果外部程序执行时间非常短,几乎不需要等待时,用cmd.CombineOutput()代替cmd.Run()即可捕获输出。

cmd := exec.Command("./test")
out, _ = cmd.CombineOutput()
fmt.Print(string(out))

4. 如何执行一个字符串型的命令

exec.Command()接收的参数是命令行拆开的多个参数,并不是一个字符串。exec.Command("ls -l")会报错,需要拆开写exec.Command("ls", "-l")。有一种比较省事的写法,调用sh -c xxx来执行。如下:

cmd := exec.Command("sh", "-c", cmd_string)

5. Run拆成Start和Wait

默认cmd.Run会等待外部程序运行结束才返回。我们可以把cmd.Run()拆成两句cmd.Start()cmd.Wait()

cmd := exec.Command("sh", "-c", cmd_string)
cmd.Start()
do_someting()
cmd.Wait()

6. 利用goroutine来并行执行

我们先把包一个运行外部命令的函数:

func run_cmd (cmd_string string) {
	fmt.Printf("cmd start...\n")
	cmd := exec.Command("sh", "-c", cmd_string)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
	fmt.Printf("cmd done\n")
}

然后在主程序里內并行运行两个外部程序,并等待60s,待外部程序执行完。

func main(){
	go run_cmd("./test1")
	go run_cmd("./test2")
	//wait 60s
	time.Sleep(time.Duration(60) * time.Second)
}

这个例子里,强行等待60s并不怎么合理,因为我们通常不知道外部程序到底需要多长时间。

有一种更优雅的方式,用sync.WaitGroup来同步。启动时waitgroup加一,结束waitgroup减一。wg.Wait()会一直等,直到wg=0。

package main

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

var wg sync.WaitGroup

func run_cmd (cmd_string string, id int) {
	//外部程序执行完,wg减1
	defer wg.Done()
	
	fmt.Printf("cmd(%d) start...\n", id)
	cmd := exec.Command("sh", "-c", cmd_string)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
	fmt.Printf("cmd(%d) done\n", id)
}

func main() {
	//启动第一个外部程序
	wg.Add(1)
	go run_cmd("./test", 1)
	
	//启动第二个外部程序
	wg.Add(1)
	go run_cmd("./test", 2)
	
	//等待所有程序执行完
	wg.Wait()
	fmt.Println("exit")
}

7. 限制并行的数量

利用channel缓冲区的大小来限制goroutine的并发数量,缓冲区满会阻一塞程序的执行。下面是一个例子:

package main

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

var wg sync.WaitGroup
//限制并发数量为4
var ch = make(chan struct{}, 4)

func run_cmd (cmd_string string, id int) {
	defer wg.Done()
	
	fmt.Printf("cmd(%d) start...\n", id)
	cmd := exec.Command("sh", "-c", cmd_string)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
	fmt.Printf("cmd(%d) done\n", id)
	
	//删除ch
	<-ch
}

func main() {
	for i:=0; i<10; i++ {
		//创建ch
		ch <- struct{}{}
		wg.Add(1)
		go run_cmd("./test", i)
	}
	wg.Wait()
	fmt.Println("exit")
}

参考资料:

 

感谢阅读,更多文章点击这里:【专栏:web开发笔记】
最新20篇 开设专栏