Go語言exec包的具體使用
在 Go 中執(zhí)行外部命令、調(diào)用系統(tǒng)程序,核心就是使用標(biāo)準(zhǔn)庫 os/exec。它能啟動進程、傳遞參數(shù)、讀寫輸入輸出、控制超時,是腳本化、自動化、調(diào)用第三方工具的必備能力。
本文實例講解 exec 包常用用法。
一、exec 包核心概念
os/exec 用于創(chuàng)建和控制子進程,與直接系統(tǒng)調(diào)用不同:
- 自帶安全處理,不會自動注入 Shell,更安全
- 支持分離
Stdout/Stderr/Stdin - 可等待、殺死、設(shè)置超時、獲取退出碼
- 多平臺通用
常用結(jié)構(gòu)與函數(shù):
exec.Command(name, arg...):創(chuàng)建命令對象cmd.Run():運行并等待完成cmd.Output():運行并返回標(biāo)準(zhǔn)輸出cmd.CombinedOutput():返回 stdout + stderrcmd.Start()+cmd.Wait():異步啟動,異步等待cmd.StdoutPipe():實時讀取輸出
二、最簡單示例:執(zhí)行命令并獲取輸出
1. 獲取命令輸出(Output)
package main
import (
"fmt"
"os/exec"
)
func main() {
// Linux/macOS
cmd := exec.Command("echo", "hello go exec")
// Windows: cmd := exec.Command("cmd", "/c", "echo hello go exec")
output, err := cmd.Output()
if err != nil {
fmt.Printf("執(zhí)行失敗: %v\n", err)
return
}
fmt.Printf("輸出結(jié)果: %s\n", output)
}
Output()會在命令非0退出碼時返回錯誤- 返回的
output是[]byte,直接轉(zhuǎn)字符串即可
2. 獲取 stdout + stderr(CombinedOutput)
當(dāng)需要把日志和錯誤一起收集時使用:
output, err := cmd.CombinedOutput()
三、只運行不獲取輸出(Run)
只關(guān)心命令是否成功,不關(guān)心輸出:
cmd := exec.Command("sleep", "1")
err := cmd.Run()
if err != nil {
fmt.Println("執(zhí)行失敗:", err)
}
四、實時輸出(不等待緩沖區(qū)滿)
以前遇到過坑:命令輸出很長時,Output() 會阻塞。
正確做法是用管道實時讀取:
package main
import (
"io"
"os"
"os/exec"
)
func main() {
// Linux/macOS
cmd := exec.Command("ping", "192.168.0.1", "-c", "3")
// Windows: exec.Command("ping", "192.168.0.1", "-n", "3")
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
cmd.Stderr = os.Stderr // 錯誤直接輸出
if err := cmd.Start(); err != nil {
panic(err)
}
// 實時讀取輸出
io.Copy(os.Stdout, stdout)
// 等待命令結(jié)束
if err := cmd.Wait(); err != nil {
panic(err)
}
}
Start():異步啟動Wait():等待子進程退出io.Copy:實時流輸出
五、給命令傳輸入(Stdin)
向子進程輸入內(nèi)容:
package main
import (
"os/exec"
"strings"
)
func main() {
// 將字符串作為標(biāo)準(zhǔn)輸入傳給 cat
cmd := exec.Command("cat")
cmd.Stdin = strings.NewReader("我是輸入內(nèi)容\nhello exec")
output, err := cmd.CombinedOutput()
if err != nil {
panic(err)
}
println(string(output))
}
六、設(shè)置工作目錄、環(huán)境變量
cmd := exec.Command("ls")
cmd.Dir = "/tmp" // 工作目錄
cmd.Env = []string{ // 環(huán)境變量
"PATH=/usr/bin:/bin",
"MY_ENV=test",
}
七、帶超時的安全調(diào)用
防止命令卡死:
package main
import (
"context"
"os/exec"
"time"
)
func main() {
// 5秒超時
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 傳入ctx
cmd := exec.CommandContext(ctx, "sleep", "10")
err := cmd.Run()
if err != nil {
// 超時會返回 ctx 錯誤
println("命令結(jié)束:", err)
}
}
八、獲取退出碼
Go 沒有直接返回 ExitCode(),需要類型斷言:
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
fmt.Println("退出碼:", exitErr.ExitCode())
}
}
九、坑點
不要手動拼接命令字符串
錯誤:
exec.Command("echo hello world") // 錯
正確:
exec.Command("echo", "hello", "world")
Windows 要調(diào)用 cmd /c
exec.Command("cmd", "/c", "dir")
輸出亂碼
Windows 下命令多為 GBK,需要轉(zhuǎn)碼。
package main
import (
"bytes"
"io"
"os/exec"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
func main() {
// Windows 執(zhí)行 ipconfig
cmd := exec.Command("ipconfig")
var buf bytes.Buffer
// 輸出:GBK → UTF-8
cmd.Stdout = transform.NewWriter(&buf, simplifiedchinese.GBK.NewDecoder())
cmd.Stderr = transform.NewWriter(&buf, simplifiedchinese.GBK.NewDecoder())
_ = cmd.Run()
println(buf.String()) // 中文不亂碼
}
Output() 會阻塞
大量輸出必須用StdoutPipe實時消費。不處理 Stdout/Stderr 可能導(dǎo)致死鎖
輸出緩沖區(qū)滿會阻塞子進程,程序卡住。
package main
import (
"os/exec"
)
// 這個程序會 100% 卡死!
func main() {
// 生成大量輸出的命令
// Windows: cmd /c "echo 大量行&for /L %i in (1,1,5000) do @echo 這是一行測試數(shù)據(jù)"
cmd := exec.Command("cmd", "/c", "echo 大量行&for /L %i in (1,1,5000) do @echo 這是一行測試數(shù)據(jù)")
// 關(guān)鍵錯誤:不設(shè)置 Stdout、Stderr
// 管道緩沖區(qū)會被填滿,子進程阻塞,cmd.Run() 永遠不返回
err := cmd.Run()
println("永遠到不了這里", err)
}
到此這篇關(guān)于Go語言exec包的具體使用的文章就介紹到這了,更多相關(guān)Go語言 exec包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于go-cqhttp與Flask搭建定制機器人項目實戰(zhàn)示例
這篇文章主要為大家介紹了基于go-cqhttp與Flask搭建定制機器人項目實戰(zhàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

