golang封裝一個(gè)執(zhí)行命令行的函數(shù)(return?stderr/stdout/exitcode)示例代碼
啟動(dòng)shell來(lái)執(zhí)行命令行
在 Go 語(yǔ)言中,您可以使用 os/exec 包來(lái)執(zhí)行外部命令,不通過(guò)調(diào)用 shell,并且能夠獲得進(jìn)程的退出碼、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出。下面是一個(gè)封裝好的函數(shù) runCommand,它符合您的需求:
package main
import (
"bytes"
"fmt"
"os/exec"
)
// runCommand 函數(shù)接受一個(gè)命令行字符串,執(zhí)行它并返回退出碼、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出
func runCommand(cmdline string) (exitCode int, stdout string, stderr string, err error) {
// 解析命令行并準(zhǔn)備執(zhí)行
cmd := exec.Command("sh", "-c", cmdline)
var outBuffer, errBuffer bytes.Buffer
cmd.Stdout = &outBuffer
cmd.Stderr = &errBuffer
// 執(zhí)行命令
err = cmd.Run()
stdout = outBuffer.String()
stderr = errBuffer.String()
// 獲取退出碼。如果命令執(zhí)行出錯(cuò),嘗試獲取退出碼
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
exitCode = exitError.ExitCode()
} else {
// 如果出現(xiàn)非 ExitError 類型錯(cuò)誤,如執(zhí)行文件不存在或無(wú)執(zhí)行權(quán)限
return 0, stdout, stderr, err
}
} else {
// 命令成功執(zhí)行,退出碼為 0
exitCode = 0
}
return exitCode, stdout, stderr, nil
}
func main() {
// 測(cè)試 runCommand 函數(shù)
exitCode, stdout, stderr, err := runCommand("ls -a")
if err != nil {
fmt.Printf("命令執(zhí)行出錯(cuò): %v\n", err)
}
fmt.Printf("退出碼: %d\n標(biāo)準(zhǔn)輸出: %s\n標(biāo)準(zhǔn)錯(cuò)誤輸出: %s\n", exitCode, stdout, stderr)
}這里的 runCommand 函數(shù)接受一個(gè)命令行字符串,執(zhí)行這條命令,并返回相關(guān)運(yùn)行結(jié)果。這個(gè)函數(shù)使用 exec.Command 來(lái)避免調(diào)用 shell。我們通過(guò)將命令傳給 sh 的 -c 參數(shù),間接地執(zhí)行命令,但繞過(guò)了可執(zhí)行文件的正常尋找機(jī)制來(lái)防止 shell 注入問(wèn)題。
結(jié)果輸出會(huì)分別寫入到 bytes.Buffer,這樣您就可以讀取并將其轉(zhuǎn)換為字符串。如果存在錯(cuò)誤,且錯(cuò)誤為 *exec.ExitError 類型,那么說(shuō)明命令返回了非零退出碼,這時(shí)我們可以獲取并返回這個(gè)退出碼;如果是其他類型的錯(cuò)誤,則直接返回錯(cuò)誤。
這個(gè)函數(shù)的返回值包含了進(jìn)程的退出碼,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出的字符串表示,以及一個(gè)可能的錯(cuò)誤值。如果命令成功執(zhí)行(即便其退出碼非零),則錯(cuò)誤值為 nil。如果函數(shù)中出現(xiàn)了 Go 運(yùn)行時(shí)錯(cuò)誤,比如命令不存在或無(wú)法啟動(dòng),那么會(huì)返回這個(gè)錯(cuò)誤。
不啟動(dòng)shell的方式
確實(shí),前面的示例通過(guò)調(diào)用 sh 間接運(yùn)行命令,依然會(huì)啟動(dòng) shell。若要避免這種情況,我們會(huì)直接分解命令行字符串為命令名和參數(shù),然后不通過(guò) shell 直接執(zhí)行這個(gè)命令。以下是經(jīng)過(guò)修改的 runCommand 函數(shù),它將使用 strings.Fields 來(lái)解析傳入的命令行字符串,并且不會(huì)啟動(dòng) shell。
package main
import (
"bytes"
"fmt"
"os/exec"
"strings"
)
// runCommand 函數(shù)接受一個(gè)命令行字符串,執(zhí)行它(無(wú) shell 啟動(dòng))并返回退出碼、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出
func runCommand(cmdline string) (exitCode int, stdout string, stderr string, err error) {
// 使用 strings.Fields 解析命令行,分離出命令和參數(shù)
args := strings.Fields(cmdline)
if len(args) == 0 {
return 0, "", "", fmt.Errorf("no command provided")
}
cmdName := args[0]
cmdArgs := args[1:]
// 準(zhǔn)備執(zhí)行不經(jīng)過(guò) shell 的命令
cmd := exec.Command(cmdName, cmdArgs...)
var outBuffer, errBuffer bytes.Buffer
cmd.Stdout = &outBuffer
cmd.Stderr = &errBuffer
// 執(zhí)行命令
err = cmd.Run()
stdout = outBuffer.String()
stderr = errBuffer.String()
// 獲取退出碼。如果命令執(zhí)行出錯(cuò),嘗試獲取退出碼
if exitErr, ok := err.(*exec.ExitError); ok {
err = nil // 清理錯(cuò)誤,我們已經(jīng)獲取了退出碼
exitCode = exitErr.ExitCode()
} else if err != nil {
// 如果出現(xiàn)非 ExitError 類型錯(cuò)誤,如執(zhí)行文件不存在或無(wú)執(zhí)行權(quán)限
exitCode = -1
} else {
// 命令成功執(zhí)行,退出碼為 0
exitCode = 0
}
return exitCode, stdout, stderr, err
}
func main() {
// 測(cè)試 runCommand 函數(shù)
exitCode, stdout, stderr, err := runCommand("ls -a -l -h")
if err != nil {
fmt.Printf("命令執(zhí)行出錯(cuò): %v\n", err)
}
fmt.Printf("退出碼: %d\n標(biāo)準(zhǔn)輸出: %s\n標(biāo)準(zhǔn)錯(cuò)誤輸出: %s\n", exitCode, stdout, stderr)
}請(qǐng)注意,這個(gè)函數(shù)假設(shè)命令行參數(shù)不含有引號(hào)封閉的參數(shù)或其他需要 shell 解析的元素(如通配符 * 或重定向 >)。它簡(jiǎn)單地把空白符作為參數(shù)分隔符,這是一個(gè)限制。如果你需要解析復(fù)雜的命令行字符串(包含引號(hào)或特殊字符),那么你需要一個(gè)更強(qiáng)大的解析器,或者需要以某種方式預(yù)處理命令行字符串。
因?yàn)橹苯訄?zhí)行命令而不經(jīng)由 shell,原本在 shell 中利用的特性(如通配符展開(kāi)、環(huán)境變量替換等)將不再可用。確保你的命令行字符串直接傳遞給系統(tǒng)調(diào)用是安全的。如果需要 shell 特性,那么你可能還需要調(diào)用 shell,但這正是你想要避免的。
關(guān)于類型斷言的表達(dá)式err.(*exec.ExitError)
在 Go 語(yǔ)言中, err.(*exec.ExitError) 是一個(gè)類型斷言的表達(dá)式。類型斷言用于檢查變量 err 是否為特定類型,或者能否轉(zhuǎn)換為特定類型。在這種情況下,類型斷言檢查 err 是否可以被斷言為 *exec.ExitError 類型。
exec.ExitError 是 os/exec 包中的一個(gè)結(jié)構(gòu)體類型,它實(shí)現(xiàn)了 error 接口,并包含了退出錯(cuò)誤的相關(guān)信息。當(dāng)一個(gè)外部命令運(yùn)行結(jié)束并返回一個(gè)非零退出狀態(tài)時(shí),os/exec 包的 Run 或 Wait 函數(shù)會(huì)返回一個(gè) error 類型的值,這通常就是一個(gè) *exec.ExitError 類型的實(shí)例。
一個(gè)類型斷言通常具有以下兩種形式:
- x.(T):這種形式用于斷言變量
x是否為類型T。如果斷言成功,就會(huì)得到x的類型T的值。如果失敗,則會(huì)產(chǎn)生一個(gè)運(yùn)行時(shí)錯(cuò)誤。因此,使用這種形式時(shí)必須非常確定x能夠斷言為類型T。 - x, ok := x.(T):這種類型斷言形式比較安全,因?yàn)槿绻麛嘌允。粫?huì)拋出錯(cuò)誤,而是將
ok的值設(shè)為false,同時(shí)x的值會(huì)被設(shè)為類型T的零值。在你不確定x的類型是否為T或者你想要安全地檢查類型時(shí),應(yīng)該使用這種形式。
在提供的代碼示例中,我們使用了第二種形式的類型斷言:
if exitErr, ok := err.(*exec.ExitError); ok {
exitCode = exitErr.ExitCode()
// ...
}這里,我們嘗試將 error 值 err 斷言為類型 *exec.ExitError。如果斷言成功(表示外部命令執(zhí)行失敗,并返回了一個(gè)非零退出代碼),變量 ok 的值將為 true,exitErr 將為 *exec.ExitError 類型,我們可以通過(guò)它的 ExitCode() 方法獲取實(shí)際的退出代碼。如果斷言失?。?code>err 不是 *exec.ExitError 類型),那么 ok 為 false,這通常表示 err 是另一種類型的錯(cuò)誤或 nil。
到此這篇關(guān)于golang封裝一個(gè)執(zhí)行命令行的函數(shù)(return stderr/stdout/exitcode)的文章就介紹到這了,更多相關(guān)golang執(zhí)行命令行的函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
gin自定義中間件解決requestBody不可重讀(請(qǐng)求體取值)
這篇文章主要介紹了gin自定義中間件解決requestBody不可重讀,確保控制器能夠獲取請(qǐng)求體值,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
詳解Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理
這篇文章主要為大家詳細(xì)介紹了Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11
go語(yǔ)言程序cpu過(guò)高問(wèn)題排查的方法詳解
使用golang進(jìn)行復(fù)雜的組合運(yùn)算,導(dǎo)致CPU占用率非常高,下面這篇文章主要給大家介紹了關(guān)于go語(yǔ)言程序cpu過(guò)高問(wèn)題排查的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
golang使用sync.singleflight解決熱點(diǎn)緩存穿透問(wèn)題
在go的sync包中,有一個(gè)singleflight包,里面有一個(gè)?singleflight.go文件,代碼加注釋,一共200行出頭,通過(guò)?singleflight可以很容易實(shí)現(xiàn)緩存和去重的效果,避免重復(fù)計(jì)算,接下來(lái)我們就給大家詳細(xì)介紹一下sync.singleflight如何解決熱點(diǎn)緩存穿透問(wèn)題2023-07-07
Go語(yǔ)言基礎(chǔ)知識(shí)點(diǎn)介紹
在本篇文章里小編給大家整理的是一篇關(guān)于Go語(yǔ)言基礎(chǔ)知識(shí)點(diǎn)介紹內(nèi)容,有興趣的朋友們可以跟著學(xué)習(xí)參考下。2021-07-07
Go語(yǔ)言Grpc?Stream的實(shí)現(xiàn)
本文主要介紹了Go語(yǔ)言Grpc?Stream的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
goroutine?泄漏和避免泄漏實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了goroutine?泄漏和避免泄漏實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Go語(yǔ)言基礎(chǔ)switch條件語(yǔ)句基本用法及示例詳解
這篇文章主要為大家介紹了Go語(yǔ)言基礎(chǔ)switch條件語(yǔ)句基本用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11

