如何在 Go 中操作系統

閱讀時間約 18 分鐘


raw-image

👨‍💻 簡介

昨天講到 os package,今天繼續補充 os package底下的 exec package,這個package主要用來執行外部指令和處理指令的輸入和輸出,包括如何設定指令、執行指令以及處理輸出等等。

設定指令

要執行外部指令,首先需要設定要執行的主要指令:

  • exec.Command:接受一個指令名稱和多個參數,並返回一個 Cmd 對象。

語法如下:

func exec.Command(name string, arg ...string) *exec.Cmd

第一個參數是放要執行的命令名稱,第二個則是放參數。 接著來看看exec.Cmd是甚麼東西:

type Cmd struct {
Path string // the path of the command to run
Args []string // holds command line arguments
Env []string // the environment of the process
Dir string // the working directory of the command.
Stdin io.Reader // the process's standard input.
Stdout io.Writer // the process's standard output and error.
Stderr io.Writer
....
}

可以看到exec.Command會回傳一個Cmd的struct。

以下是一個設定指令的簡單範例:

package main

import (
"fmt"
"os/exec"
)

func main() {

cmd := exec.Command("ls", "-al")
err := cmd.Run()
if err != nil {
fmt.Printf("failed to call cmd.Run(): %v", err)
}
}

執行完沒有任何輸出是正常的,因為這個範例並沒有將標準輸出印出。如果要印出執行結果將cmd.Run改為cmd.Output即可:

package main

import (
"fmt"
"os/exec"
)

func main() {

cmd := exec.Command("ls", "-al")
output, err := cmd.Output()
if err != nil {
fmt.Printf("failed to call cmd.Output(): %v", err)
return
}

fmt.Println("Command Output:")
fmt.Println(string(output))
}

接著來快速介紹Cmd struct常用的欄位:

package main

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

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

// 設定執行位置
cmd.Dir = "/"

// 將標準輸出和標準錯誤輸出到終端機
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// 執行並等待其完成
err := cmd.Run()
if err != nil {
fmt.Println(err)
return
}
}

執行指令

  • cmd.Run:執行指令並會等待指令完成,是cmd.Startcmd.Wait的結合。
  • cmd.Start:執行指令但不會等待指令完成。
  • cmd.Wait:等待已經執行的指令,會阻塞程式,職到指令執行完畢。
package main

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

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

// 執行指令
err := cmd.Start()
if err != nil {
fmt.Println(err)
return
}
// 等待指令完成
err = cmd.Wait()
if err != nil {
log.Fatal(err)
}
}

處理輸出

  • cmd.Output:取得執行的標準輸出。
  • cmd.CombinedOutput:取得執行的標準輸出和標準錯誤的合併結果。

取得標準輸出

package main

import (
"fmt"
"os/exec"
)

func main() {
cmd := exec.Command("echo", "Hello, World!")

// 取得執行的標準輸出
output, err := cmd.Output()
if err != nil {
fmt.Println("failed to call cmd.Output(): %v", err)
return
}

fmt.Println("Command Output:")
fmt.Println(string(output))
}

取得標準輸出和標準錯誤的合併結果

package main

import (
"fmt"
"os/exec"
)

func main() {
cmd := exec.Command("ls", "non-existent-directory")

// 取得執行的標準輸出和標準錯誤的合併結果
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println("Error:", err)
return
}

fmt.Println("Command Output:")
fmt.Println(string(output))
}

輸出的多種方式

因為cmd 的struct包含以下三個欄位,分別代表執行的標準輸入,標準輸出以及標準錯誤輸出,而io.Reader以及io.Writer皆為interface,因此只要滿足interface method的struct,即可完成讀取跟寫入

type Cmd struct {
Stdin io.Reader // the process's standard input.
Stdout io.Writer // the process's standard output and error.
Stderr io.Writer
....
}
  • 寫入檔案
package main

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

func main() {
// 建立一個檔案,用於存儲標準輸出
outFile, err := os.Create("output.txt")
if err != nil {
fmt.Println(err)
return
}
defer outFile.Close()

// 建立一個檔案,用於存儲標準錯誤
errFile, err := os.Create("error.txt")
if err != nil {
fmt.Println(err)
return
}
defer errFile.Close()

// 設定執行指令,將標準輸出和標準錯誤重定向到檔案
cmd := exec.Command("ls", "-l")
cmd.Stdout = outFile
cmd.Stderr = errFile

// 執行並等待其完成
err = cmd.Run()
if err != nil {
fmt.Println(err)
return
}
}
  • 寫到終端
package main

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

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

// 將標準輸出和標準錯誤輸出到終端機
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// 執行指令
err := cmd.Start()
if err != nil {
fmt.Println(err)
return
}

// 等待指令完成
err = cmd.Wait()
if err != nil {
log.Fatal(err)
}
}

因os.Create、os.Stdout和os.Stderr都會返回一個File struct,而該struct有滿足io.Writer的method -> Write,因此可以將標準輸出以及標準錯誤輸出給寫入檔案或是終端裡。

處理輸入

透過wc指令讀取main.go檔案,並計算總共多少行。

package main

import (
"log"
"os"
"os/exec"
)

func main() {
stdin, err := os.Open("main.go")
if err != nil {
log.Fatalf("failed to open file: %v", err)
}
cmd := exec.Command("wc", "-l")
cmd.Stdin = stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatalf("failed to call cmd.Run(): %v", err)
}
}

Pipeline操作

  • cmd.StderrPipe:返回一個連接到執行的標準錯誤的pipeline。
  • cmd.StdinPipe:返回一個連接到執行的標準輸入的pipeline。
  • cmd.StdoutPipe:返回一個連接到執行的標準輸出的pipeline。
package main

import (
"log"
"os"
"os/exec"
)

func main() {
cmdCat := exec.Command("cat", "main.go")
cmdWC := exec.Command("wc", "-l")

// 建立一個pipeline,將 cat main.go 的輸出內容存放到 catout
catout, err := cmdCat.StdoutPipe()
if err != nil {
log.Fatalf("failed to get StdoutPipe of cat: %v", err)
}

cmdWC.Stdin = catout // 將 catout 當作 wc -l 的輸入
cmdWC.Stdout = os.Stdout // 將 wc -l 的結果印到終端機上

err = cmdCat.Start()
if err != nil {
log.Fatalf("failed to call cmdCat.Start():%v", err)
}

err = cmdWC.Start()
if err != nil {
log.Fatalf("failed to call cmdWC.Start():%v", err)
}

cmdCat.Wait()
cmdWC.Wait()
}

通用pipeline方法

package main

import (
"fmt"
"os/exec"
)

func main() {
cmdCat := exec.Command("cat", "main.go")
cmdWC := exec.Command("wc", "-l")
data, err := pipeCommands(cmdCat, cmdWC)
if err != nil {
fmt.Printf("failed to call pipeCommands(): %v", err)
}
fmt.Printf("output: %s", data)
}

func pipeCommands(commands ...*exec.Cmd) ([]byte, error) {
for i, command := range commands[:len(commands)-1] {
out, err := command.StdoutPipe()
if err != nil {
return nil, err
}
command.Start()
commands[i+1].Stdin = out
}
final, err := commands[len(commands)-1].Output()
if err != nil {
return nil, err
}
return final, nil
}

bash pipeline

cmd := exec.Command("bash", "-c", "cat main.go| wc -l")
data, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("failed to call pipeCommands(): %v", err)
}
log.Printf("output: %s", data)

📚 參考資料


17會員
83內容數
golang
留言0
查看全部
發表第一個留言支持創作者!
Alan的開發者天地 的其他內容
👨‍💻 簡介 今天快速介紹一下對檔案的操作所使用的package os,包括檔案和資料夾操作等。 檔案和資料夾操作 os package 可以執行各種檔案和資料夾操作,如建立、讀取、寫入、刪除檔案,以及取得資料夾內容等。
👨‍💻簡介 在 Go 語言中,reflect package是用來檢查和操作變數的type、value和struct。常見用法有檢察 type、調用方法,以及修改變數的value。今天簡單介紹 reflect package的主要功能、使用方法和常見用法。
👨‍💻 簡介 今天的encoding/json package是我日常在開發web時很常用到的package之一,主要是用來將Go struct和 JSON 之間進行轉換。主要功能為資料序列化(marshalling)和反序列化(unmarshalling)。
👨‍💻簡介 要處理日期和時間,就必須知道這個Package -> time,Go提供了內建的timePackage。 今天主要介紹time的功能,包括時間操作、格式化等等,以及常見用法。
👨‍💻簡介 要印出Hello World,就會碰到這個Package -> fmt,今天來簡單整理一下fmt的作用、格式化的控制與常見用法。
👨‍💻簡介 昨天講到Goroutine的橋梁aka傳話筒 — Channel,那要怎麼知道對方有收到訊息,我的紙條有送到對方手上呢? 今天就是要來介紹幾種Goroutine的確定完成工作的幾種方式。
👨‍💻 簡介 今天快速介紹一下對檔案的操作所使用的package os,包括檔案和資料夾操作等。 檔案和資料夾操作 os package 可以執行各種檔案和資料夾操作,如建立、讀取、寫入、刪除檔案,以及取得資料夾內容等。
👨‍💻簡介 在 Go 語言中,reflect package是用來檢查和操作變數的type、value和struct。常見用法有檢察 type、調用方法,以及修改變數的value。今天簡單介紹 reflect package的主要功能、使用方法和常見用法。
👨‍💻 簡介 今天的encoding/json package是我日常在開發web時很常用到的package之一,主要是用來將Go struct和 JSON 之間進行轉換。主要功能為資料序列化(marshalling)和反序列化(unmarshalling)。
👨‍💻簡介 要處理日期和時間,就必須知道這個Package -> time,Go提供了內建的timePackage。 今天主要介紹time的功能,包括時間操作、格式化等等,以及常見用法。
👨‍💻簡介 要印出Hello World,就會碰到這個Package -> fmt,今天來簡單整理一下fmt的作用、格式化的控制與常見用法。
👨‍💻簡介 昨天講到Goroutine的橋梁aka傳話筒 — Channel,那要怎麼知道對方有收到訊息,我的紙條有送到對方手上呢? 今天就是要來介紹幾種Goroutine的確定完成工作的幾種方式。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
覺得你抗壓力好強,工作一直被念但還是努力工作。 今天收到了一個意外的鼓勵,朋友說:「你抗壓力真強,工作老是被念,但你還是那麼努力。」聽到這句話,心裡一陣溫暖。然而,在這之前,我剛因為工作上的一個小錯誤而被主管批評。事情是這樣的,我在檢查數值時,沒有發現它超出了規格。對於這樣一個「簡單的小事」,
Thumbnail
在 TikTok 上提升影片曝光率不僅需創意,還需有效的 SEO 策略。透過使用工具如 Answer The Public 找到熱門問題,運用五個W(Who、What、When、Where、Why)內容創作法,保持真實性以及合理運用關鍵字和影片編輯技巧,顯著提升內容可見性,吸引更多觀眾並長期成功。
Thumbnail
今天要來和大家分享亡靈節活動體驗,以及Pokémon GO City Safari即將在台南舉辦的購票資訊。
Thumbnail
👨‍💻 簡介 在處理string時,正則表達式是一個非常有用的工具。Go語言的regexp package 可以使用正則表達式,用來執行如檢查string是否匹配某個模式、提取匹配的subString等操作。
Thumbnail
👨‍💻 簡介 一開始介紹基本資料型別時有稍微提到一點string的處理,今天介紹string的一些操作,像是檢查的功能、修改的功能、比較的功能等等。
Thumbnail
「一眼望穿的生活沒有魅力,而迷霧本身即是生命的常態。」——《一隅有花 Withering Calendar》 原本中午就因為Q寶打校隊的來回接送,導致討厭曬太陽的我苦不堪言,我知道我心裡也很矛盾,看著這麼小的孩子找到他的熱情,當然替他開心,也很想支持他,但這些都不該是拿委屈或勉強我自己來交換...
Thumbnail
有多種情況下我們需要從API取消請求: 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。 訂閱取消。 ......等等 這將對對這個系統沒有信心的使用者產生影響。因
Thumbnail
Windows電腦中,我們可以利用內建工具"工作排程器"去預設電腦重新啟動或登入時,自動執行重要程式,避免遺漏程式忘記,導致連動程式的系統不能使用。 以下是教學步驟: 步驟一: 開啟Windows電腦中內建工具"工作排程器" 步驟二: 將游標移至"工作排程器程式庫"按右鍵"建立工作" 步驟三:
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
覺得你抗壓力好強,工作一直被念但還是努力工作。 今天收到了一個意外的鼓勵,朋友說:「你抗壓力真強,工作老是被念,但你還是那麼努力。」聽到這句話,心裡一陣溫暖。然而,在這之前,我剛因為工作上的一個小錯誤而被主管批評。事情是這樣的,我在檢查數值時,沒有發現它超出了規格。對於這樣一個「簡單的小事」,
Thumbnail
在 TikTok 上提升影片曝光率不僅需創意,還需有效的 SEO 策略。透過使用工具如 Answer The Public 找到熱門問題,運用五個W(Who、What、When、Where、Why)內容創作法,保持真實性以及合理運用關鍵字和影片編輯技巧,顯著提升內容可見性,吸引更多觀眾並長期成功。
Thumbnail
今天要來和大家分享亡靈節活動體驗,以及Pokémon GO City Safari即將在台南舉辦的購票資訊。
Thumbnail
👨‍💻 簡介 在處理string時,正則表達式是一個非常有用的工具。Go語言的regexp package 可以使用正則表達式,用來執行如檢查string是否匹配某個模式、提取匹配的subString等操作。
Thumbnail
👨‍💻 簡介 一開始介紹基本資料型別時有稍微提到一點string的處理,今天介紹string的一些操作,像是檢查的功能、修改的功能、比較的功能等等。
Thumbnail
「一眼望穿的生活沒有魅力,而迷霧本身即是生命的常態。」——《一隅有花 Withering Calendar》 原本中午就因為Q寶打校隊的來回接送,導致討厭曬太陽的我苦不堪言,我知道我心裡也很矛盾,看著這麼小的孩子找到他的熱情,當然替他開心,也很想支持他,但這些都不該是拿委屈或勉強我自己來交換...
Thumbnail
有多種情況下我們需要從API取消請求: 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。 訂閱取消。 ......等等 這將對對這個系統沒有信心的使用者產生影響。因
Thumbnail
Windows電腦中,我們可以利用內建工具"工作排程器"去預設電腦重新啟動或登入時,自動執行重要程式,避免遺漏程式忘記,導致連動程式的系統不能使用。 以下是教學步驟: 步驟一: 開啟Windows電腦中內建工具"工作排程器" 步驟二: 將游標移至"工作排程器程式庫"按右鍵"建立工作" 步驟三: