最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go os/exec使用方式實踐

 更新時間:2025年07月03日 09:39:29   作者:alden_ygq  
這篇文章主要介紹了Go os/exec使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

os/exec 是 Go 提供的內(nèi)置包,可以用來執(zhí)行外部命令或程序。比如,我們的主機(jī)上安裝了 redis-server 二進(jìn)制文件,那么就可以使用 os/exec 在 Go 程序中啟動 redis-server 提供服務(wù)。當(dāng)然,我們也可以使用 os/exec 執(zhí)行 ls、pwd 等操作系統(tǒng)內(nèi)置命令。本文不求內(nèi)容多么深入,旨在帶大家極速入門 os/exec 的常規(guī)使用。

os/exec 包結(jié)構(gòu)體與方法

func LookPath(file string) (string, error)
type Cmd
    func Command(name string, arg ...string) *Cmd
    func CommandContext(ctx context.Context, name string, arg ...string) *Cmd
    func (c *Cmd) CombinedOutput() ([]byte, error)
    func (c *Cmd) Environ() []string
    func (c *Cmd) Output() ([]byte, error)
    func (c *Cmd) Run() error
    func (c *Cmd) Start() error
    func (c *Cmd) StderrPipe() (io.ReadCloser, error)
    func (c *Cmd) StdinPipe() (io.WriteCloser, error)
    func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
    func (c *Cmd) String() string
    func (c *Cmd) Wait() error

Cmd 結(jié)構(gòu)體表示一個準(zhǔn)備或正在執(zhí)行的外部命令。

  • 調(diào)用函數(shù) Command 或 CommandContext 可以構(gòu)造一個 *Cmd 對象。
  • 調(diào)用 Run、StartOutput、CombinedOutput 方法可以運(yùn)行 *Cmd 對象所代表的命令。
  • 調(diào)用 Environ 方法可以獲取命令執(zhí)行時的環(huán)境變量。
  • 調(diào)用 StdinPipe、StdoutPipe、StderrPipe 方法用于獲取管道對象。
  • 調(diào)用 Wait 方法可以阻塞等待命令執(zhí)行完成。
  • 調(diào)用 String 方法返回命令的字符串形式。LookPath 函數(shù)用于搜索可執(zhí)行文件。

使用方法

package main

import (
"log"
"os/exec"
)

func main() {
// 創(chuàng)建一個命令
 cmd := exec.Command("echo", "Hello, World!")

// 執(zhí)行命令并等待命令完成
 err := cmd.Run() // 執(zhí)行后控制臺不會有任何輸出
 if err != nil {
  log.Fatalf("Command failed: %v", err)
 }
}
  • exec.Command 函數(shù)用于創(chuàng)建一個命令,函數(shù)第一個參數(shù)是命令的名稱,后面跟一個不定常參數(shù)作為這個命令的參數(shù),最終會傳遞給這個命令。
  • *Cmd.Run 方法會阻塞等待命令執(zhí)行完成,默認(rèn)情況下命令執(zhí)行后控制臺不會有任何輸出:
# 執(zhí)行程序
$ go run main.go
# 執(zhí)行完成后沒有任何輸出

可以在后臺運(yùn)行一個命令:

func main() {
 cmd := exec.Command("sleep", "3")

// 執(zhí)行命令(非阻塞,不會等待命令執(zhí)行完成)
if err := cmd.Start(); err != nil {
  log.Fatalf("Command start failed: %v", err)
  return
 }

 fmt.Println("Command running in the background...")

// 阻塞等待命令完成
if err := cmd.Wait(); err != nil {
  log.Fatalf("Command wait failed: %v", err)
  return
 }

 log.Println("Command finished")
}

實際上 Run 方法就等于 Start + Wait 方法,如下是 Run 方法源碼的實現(xiàn):

func (c *Cmd) Run() error {
 if err := c.Start(); err != nil {
  return err
 }
 return c.Wait()
}

創(chuàng)建帶有 context 的命令

os/exec 還提供了一個 exec.CommandContext 構(gòu)造函數(shù)可以創(chuàng)建一個帶有 context 的命令。那么我們就可以利用 context 的特性來控制命令的執(zhí)行了。

func main() {
 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
 defer cancel()

 cmd := exec.CommandContext(ctx, "sleep", "5")

 if err := cmd.Run(); err != nil {
  log.Fatalf("Command failed: %v\n", err) // signal: killed
 }
}

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
2025/01/14 23:54:20 Command failed: signal: killed
exit status 1

當(dāng)命令執(zhí)行超時會收到 killed 信號自動取消。

獲取命令的輸出

無論是調(diào)用 *Cmd.Run 還是 *Cmd.Start 方法,默認(rèn)情況下執(zhí)行命令后控制臺不會得到任何輸出。

可以使用 *Cmd.Output 方法來執(zhí)行命令,以此來獲取命令的標(biāo)準(zhǔn)輸出:

func main() {
 // 創(chuàng)建一個命令
 cmd := exec.Command("echo", "Hello, World!")

 // 執(zhí)行命令,并獲取命令的輸出,Output 內(nèi)部會調(diào)用 Run 方法
 output, err := cmd.Output()
 if err != nil {
  log.Fatalf("Command failed: %v", err)
 }

 fmt.Println(string(output)) // Hello, World!
}

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
Hello, World!

獲取組合的標(biāo)準(zhǔn)輸出和錯誤輸出

*Cmd.CombinedOutput 方法能夠在運(yùn)行命令后,返回其組合的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤輸出:

func main() {
// 使用一個命令,既產(chǎn)生標(biāo)準(zhǔn)輸出,也產(chǎn)生標(biāo)準(zhǔn)錯誤輸出
 cmd := exec.Command("sh", "-c", "echo 'This is stdout'; echo 'This is stderr' >&2")

// 獲取 標(biāo)準(zhǔn)輸出 + 標(biāo)準(zhǔn)錯誤輸出 組合內(nèi)容
 output, err := cmd.CombinedOutput()
 if err != nil {
  log.Fatalf("Command execution failed: %v", err)
 }

// 打印組合輸出
 fmt.Printf("Combined Output:\n%s", string(output))
}

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
Combined Output:
This is stdout
This is stderr

設(shè)置標(biāo)準(zhǔn)輸出和錯誤輸出

可以利用 *Cmd 對象的 Stdout 和 Stderr 屬性,重定向標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤輸出到當(dāng)前進(jìn)程:

func main() {
 cmd := exec.Command("ls", "-l")

 // 設(shè)置標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤輸出到當(dāng)前進(jìn)程,執(zhí)行后可以在控制臺看到命令執(zhí)行的輸出
 cmd.Stdout = os.Stdout
 cmd.Stderr = os.Stderr

 if err := cmd.Run(); err != nil {
  log.Fatalf("Command failed: %v", err)
 }
}

這樣,使用 *Cmd.Run 執(zhí)行命令后控制臺就能看到命令執(zhí)行的輸出了。

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
total 4824
-rw-r--r--  1 jianghushinian  staff       12 Jan  4 10:37 demo.log
drwxr-xr-x  3 jianghushinian  staff       96 Jan 13 09:41 examples
-rwxr-xr-x  1 jianghushinian  staff  2453778 Jan  1 15:09 main
-rw-r--r--  1 jianghushinian  staff     6179 Jan 15 09:13 main.go

使用標(biāo)準(zhǔn)輸入傳遞數(shù)據(jù)

可以使用 grep 命令接收 stdin 的數(shù)據(jù),然后在其中搜索包含指定模式的文本行:

func main() {
 cmd := exec.Command("grep", "hello")

// 通過標(biāo)準(zhǔn)輸入傳遞數(shù)據(jù)給命令
 cmd.Stdin = bytes.NewBufferString("hello world!\nhi there\n")

// 獲取標(biāo)準(zhǔn)輸出
 output, err := cmd.Output()
 if err != nil {
  log.Fatalf("Command failed: %v", err)
  return
 }

 fmt.Println(string(output)) // hello world!
}

可以將一個 io.Reader 對象賦值給 *Cmd.Stdin 屬性,來實現(xiàn)將數(shù)據(jù)通過 stdin 傳遞給外部命令。

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
hello world!

 還可以將打開的文件描述符傳給 *Cmd.Stdin 屬性:

func main() {
 file, err := os.Open("demo.log") // 打開一個文件
 if err != nil {
  log.Fatalf("Open file failed: %v\n", err)
  return
 }
 defer file.Close()

 cmd := exec.Command("cat")
 cmd.Stdin = file       // 將文件作為 cat 的標(biāo)準(zhǔn)輸入
 cmd.Stdout = os.Stdout // 獲取標(biāo)準(zhǔn)輸出

 if err := cmd.Run(); err != nil {
  log.Fatalf("Command failed: %v", err)
 }
}

只要是 io.Reader 對象即可。

設(shè)置和使用環(huán)境變量

*Cmd 的 Environ 方法可以獲取環(huán)境變量,Env 屬性則可以設(shè)置環(huán)境變量:

func main() {
 cmd := exec.Command("printenv", "ENV_VAR")

 log.Printf("ENV: %+v\n", cmd.Environ())

// 設(shè)置環(huán)境變量
 cmd.Env = append(cmd.Environ(), "ENV_VAR=HelloWorld")

 log.Printf("ENV: %+v\n", cmd.Environ())

// 獲取輸出
 output, err := cmd.Output()
 if err != nil {
  log.Fatalf("Command failed: %v", err)
 }

 fmt.Println(string(output)) // HelloWorld
}

這段代碼輸出結(jié)果與執(zhí)行環(huán)境相關(guān),此處不演示執(zhí)行結(jié)果了,你可以自行嘗試。

不過最終的 output 輸出結(jié)果一定是 HelloWorld

使用管道

os/exec 支持管道功能,*Cmd 對象提供的 StdinPipe、StdoutPipe、StderrPipe 三個方法用于獲取管道對象。故名思義,三者分別對應(yīng)標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯誤輸出的管道對象。

使用示例如下:

func main() {
// 命令中使用了管道
 cmdEcho := exec.Command("echo", "hello world\nhi there")

 outPipe, err := cmdEcho.StdoutPipe()
 if err != nil {
  log.Fatalf("Command failed: %v", err)
 }

// 注意,這里不能使用 Run 方法阻塞等待,應(yīng)該使用非阻塞的 Start 方法
 if err := cmdEcho.Start(); err != nil {
  log.Fatalf("Command failed: %v", err)
 }

 cmdGrep := exec.Command("grep", "hello")
 cmdGrep.Stdin = outPipe
 output, err := cmdGrep.Output()
 if err != nil {
  log.Fatalf("Command failed: %v", err)
 }

 fmt.Println(string(output)) // hello world
}

首先創(chuàng)建一個用于執(zhí)行 echo 命令的 *Cmd 對象 cmdEcho,并調(diào)用它的 StdoutPipe 方法獲得標(biāo)準(zhǔn)輸出管道對象 outPipe;

然后調(diào)用 Start 方法非阻塞的方式執(zhí)行 echo 命令;

接著創(chuàng)建一個用于執(zhí)行 grep 命令的 *Cmd 對象 cmdGrep,將 cmdEcho 的標(biāo)準(zhǔn)輸出管道對象賦值給 cmdGrep.Stdin 作為標(biāo)準(zhǔn)輸入,這樣,兩個命令就通過管道串聯(lián)起來了;

最終通過 cmdGrep.Output 方法拿到 cmdGrep 命令的標(biāo)準(zhǔn)輸出。

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
hello world

使用bash -c執(zhí)行復(fù)雜命令

如果你不想使用 os/exec 提供的管道功能,那么在命令中直接使用管道符 |,也可以實現(xiàn)同樣功能。

不過此時就需要使用 sh -c 或者 bash -c 等 Shell 命令來解析執(zhí)行更復(fù)雜的命令了:

func main() {
 // 命令中使用了管道
 cmd := exec.Command("bash", "-c", "echo 'hello world\nhi there' | grep hello")

 output, err := cmd.Output()
 if err != nil {
  log.Fatalf("Command failed: %v", err)
 }

 fmt.Println(string(output)) // hello world
}

這段代碼中的管道功能同樣生效。

指定工作目錄

可以通過指定 *Cmd 對象的的 Dir 屬性來指定工作目錄:

func main() {
 cmd := exec.Command("cat", "demo.log")
 cmd.Stdout = os.Stdout // 獲取標(biāo)準(zhǔn)輸出
 cmd.Stderr = os.Stderr // 獲取錯誤輸出

 // cmd.Dir = "/tmp" // 指定絕對目錄
 cmd.Dir = "." // 指定相對目錄

 if err := cmd.Run(); err != nil {
  log.Fatalf("Command failed: %v", err)
 }
}

捕獲退出狀態(tài)

上面講解了很多執(zhí)行命令相關(guān)操作,但其實還有一個很重要的點沒有講到,就是如何捕獲外部命令執(zhí)行后的退出狀態(tài)碼:

func main() {
// 查看一個不存在的目錄
 cmd := exec.Command("ls", "/nonexistent")

// 運(yùn)行命令
 err := cmd.Run()

// 檢查退出狀態(tài)
var exitError *exec.ExitError
if errors.As(err, &exitError) {
  log.Fatalf("Process PID: %d exit code: %d", exitError.Pid(), exitError.ExitCode()) // 打印 pid 和退出碼
 }
}

這里執(zhí)行 ls 命令來查看一個不存在的目錄 /nonexistent,程序退出狀態(tài)碼必然不為 0。

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
2025/01/15 23:31:44 Process PID: 78328 exit code: 1
exit status 1

搜索可執(zhí)行文件

最后要介紹的函數(shù)就只剩一個 LookPath 了,它用來搜索可執(zhí)行文件。

搜索一個存在的命令:

func main() {
 path, err := exec.LookPath("ls")
 if err != nil {
  log.Fatal("installing ls is in your future")
 }
 fmt.Printf("ls is available at %s\n", path)
}

執(zhí)行示例代碼,得到輸出如下:

 $ go run main.go
ls is available at /bin/ls

搜索一個不存在的命令:

func main() {
 path, err := exec.LookPath("lsx")
 if err != nil {
  log.Fatal(err)
 }
 fmt.Printf("ls is available at %s\n", path)
}

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
2025/01/15 23:37:45 exec: "lsx": executable file not found in $PATH
exit status 1

功能練習(xí)

介紹完了 os/exec 常用的方法和函數(shù),我們現(xiàn)在來做一個小練習(xí),使用 os/exec 來執(zhí)行外部命令 ls -l /var/log/*.log

示例如下:

func main() {
 cmd := exec.Command("ls", "-l", "/var/log/*.log")

 output, err := cmd.CombinedOutput() // 獲取標(biāo)準(zhǔn)輸出和錯誤輸出
 if err != nil {
  log.Fatalf("Command failed: %v", err)
 }

 fmt.Println(string(output))
}

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
2025/01/16 09:15:52 Command failed: exit status 1
exit status 1

執(zhí)行報錯了,這里的錯誤碼為 1,但錯誤信息并不明確。

這個報錯其實是因為 os/exec 默認(rèn)不支持通配符參數(shù)導(dǎo)致的,exec.Command 不支持直接在參數(shù)中使用 Shell 通配符(如 *),因為它不會通過 Shell 來解析命令,而是直接調(diào)用底層的程序。

要解決這個問題,可以通過顯式調(diào)用 Shell(例如 bash 或 sh),讓 Shell 來解析通配符。

比如使用 bash -c 執(zhí)行通配符命令 ls -l /var/log/*.log

func main() {
    // 使用 bash -c 來解析通配符
    cmd := exec.Command("bash", "-c", "ls -l /var/log/*.log")

    output, err := cmd.CombinedOutput() // 獲取標(biāo)準(zhǔn)輸出和錯誤輸出
    if err != nil {
        log.Fatalf("Command failed: %v", err)
    }

    fmt.Println(string(output))
}

執(zhí)行示例代碼,得到輸出如下:

$ go run main.go
-rw-r--r--  1 root  wheel         0 Oct  7 21:20 /var/log/alf.log
-rw-r--r--  1 root  wheel     11936 Jan 13 11:36 /var/log/fsck_apfs.log
-rw-r--r--  1 root  wheel       334 Jan 13 11:36 /var/log/fsck_apfs_error.log
-rw-r--r--  1 root  wheel     19506 Jan 11 18:04 /var/log/fsck_hfs.log
-rw-r--r--@ 1 root  wheel  21015342 Jan 16 09:02 /var/log/install.log
-rw-r--r--  1 root  wheel      1502 Nov  5 09:44 /var/log/shutdown_monitor.log
-rw-r-----@ 1 root  admin      3779 Jan 16 08:59 /var/log/system.log
-rw-r-----  1 root  admin    187332 Jan 16 09:05 /var/log/wifi.log

此外,我們還可以用 Go 標(biāo)準(zhǔn)庫提供的 filepath.Glob 來手動解析通配符:

func main() {
// 匹配通配符路徑
 files, err := filepath.Glob("/var/log/*.log")
 if err != nil {
  log.Fatalf("Glob failed: %v", err)
 }
 if len(files) == 0 {
  log.Println("No matching files found")
  return
 }

// 將匹配到的文件傳給 ls 命令
 args := append([]string{"-l"}, files...)
 cmd := exec.Command("ls", args...)

 cmd.Stdout = os.Stdout
 cmd.Stderr = os.Stderr

 if err := cmd.Run(); err != nil {
  log.Fatalf("Command failed: %v", err)
 }
}

filepath.Glob 函數(shù)會返回模式匹配的文件名列表,如果不匹配則返回 nil。這樣,我們就可以先解析文件名列表,再交給 exec.Command 來執(zhí)行 ls 命令了。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • GO實現(xiàn)基于命令行的簡單IPS程序代碼

    GO實現(xiàn)基于命令行的簡單IPS程序代碼

    本文介紹了入侵防御系統(tǒng)IPS的工作原理和實現(xiàn),IPS通過網(wǎng)絡(luò)流量監(jiān)控和實時響應(yīng),防止網(wǎng)絡(luò)攻擊,通過使用Go語言實現(xiàn)一個簡單的IPS示例程序,展示了如何獲取本地IP地址和探測網(wǎng)絡(luò)中其他設(shè)備的IP地址,包括如何定義和加載規(guī)則文件,以及如何檢測IP對相應(yīng)端口的訪問是否達(dá)到規(guī)定閾值
    2024-12-12
  • go語言同步教程之條件變量

    go語言同步教程之條件變量

    這篇文章主要給大家介紹了關(guān)于go語言同步教程之條件變量的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • GoLang完整實現(xiàn)快速列表

    GoLang完整實現(xiàn)快速列表

    這篇文章主要介紹了GoLang完整實現(xiàn)快速列表,列表是一種非連續(xù)的存儲容器,由多個節(jié)點組成,節(jié)點通過一些 變量 記錄彼此之間的關(guān)系,列表有多種實現(xiàn)方法,如單鏈表、雙鏈表等
    2022-12-12
  • Go語言中panic的實現(xiàn)示例

    Go語言中panic的實現(xiàn)示例

    Go語言中panic用于處理不可恢復(fù)的嚴(yán)重錯誤,觸發(fā)后終止函數(shù)并展開堆棧,需與defer配合使用recover捕獲,避免程序崩潰,具有一定的參考價值,感興趣的可以了解一下
    2025-08-08
  • Go實現(xiàn)將任何網(wǎng)頁轉(zhuǎn)化為PDF

    Go實現(xiàn)將任何網(wǎng)頁轉(zhuǎn)化為PDF

    在許多應(yīng)用場景中,可能需要將網(wǎng)頁內(nèi)容轉(zhuǎn)化為?PDF?格式,使用Go編程語言,結(jié)合一些現(xiàn)有的庫,可以非常方便地實現(xiàn)這一功能,下面我們就來看看具體實現(xiàn)方法吧
    2024-11-11
  • Go接口的用法詳解

    Go接口的用法詳解

    本文主要介紹了Go接口的用法詳解,包括定義接口、實現(xiàn)接口、使用接口、空接口等,通過接口,可以實現(xiàn)多態(tài)性,即一個對象可以實現(xiàn)多個接口,從而實現(xiàn)不同接口的行為,感興趣的可以了解一下
    2023-11-11
  • Goland和IDEA換行符的設(shè)置方式

    Goland和IDEA換行符的設(shè)置方式

    這篇文章主要介紹了Goland和IDEA換行符的設(shè)置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Golang流程控制語句的具體使用

    Golang流程控制語句的具體使用

    在編寫程序時,流程控制是必不可少的一部分,本文主要介紹了Golang流程控制語句的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • Go語言實戰(zhàn)學(xué)習(xí)之流程控制詳解

    Go語言實戰(zhàn)學(xué)習(xí)之流程控制詳解

    這篇文章主要為大家詳細(xì)介紹了Go語言中的流程控制,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Go語言有一定的幫助?,需要的朋友可以參考下
    2022-08-08
  • Golang函數(shù)的使用技巧(結(jié)合代碼示例)

    Golang函數(shù)的使用技巧(結(jié)合代碼示例)

    函數(shù)是基本的代碼塊,用于執(zhí)行一個任務(wù),這篇文章主要介紹了Golang函數(shù)使用的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2025-11-11

最新評論

四子王旗| 浙江省| 宽城| 竹山县| 古田县| 共和县| 贞丰县| 华阴市| 麟游县| 通化县| 呈贡县| 赣州市| 衡山县| 灵川县| 渝北区| 徐州市| 晋中市| 永兴县| 青海省| 阿坝县| 阳曲县| 鄂尔多斯市| 泾源县| 莆田市| 垦利县| 新源县| 十堰市| 芒康县| 藁城市| 会东县| 福泉市| 垦利县| 开远市| 济宁市| 吉隆县| 莱阳市| 富川| 丹江口市| 兰坪| 秀山| 临潭县|