在 Go 中導向標準錯誤輸出時

5 min

language: ja bn en es hi pt ru zh-cn zh-tw

您好,我是無能。

關於Go中此部分的說明文章並不多見,因此我將其記錄下來。

確認回傳值

當初有這樣的程式碼時。

func main() {
  if len(os.Args) < 2 {
    fmt.Println("Usage: go run main.go https://soulminingrig.com/")
    return
  }

  url := os.Args[1]
  title, err := fetchTitle(url)
  if err != nil {
    fmt.Printf( "Error: %v\n", err)
    return
  }

  fmt.Printf("Title: %s\n", title)
}

當然,在Go中,上述程式碼會將錯誤作為錯誤輸出,但如果我們想取得建置後執行檔的錯誤回傳值,該怎麼辦呢?

alleycat:[haturatu]:~/git/go-title$ ./go-title 
Usage: go run main.go https://soulminingrig.com/
alleycat:[haturatu]:~/git/go-title$ echo $?
0
alleycat:[haturatu]:~/git/go-title$ ./go-title http://https://soulminingrig.nodomain/
Error: failed to fetch URL: Get "http://https//soulminingrig.nodomain/": dial tcp: lookup https: no such host
alleycat:[haturatu]:~/git/go-title$ echo $?
0

所有內容都變成了標準輸出。

os.Exit指定回傳值

因此,需要使用os.Exit來指定,使其正確地被視為錯誤,修改如下。

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run main.go https://soulminingrig.com/")
		os.Exit(1)
	}

	url := os.Args[1]
	title, err := fetchTitle(url)
	if err != nil {
		fmt.Printf( "Error: %v\n", err)
		os.Exit(2)
	}

	fmt.Printf("Title: %s\n", title)
}

現在,再次建置並執行。

alleycat:[haturatu]:~/git/go-title$ ./go-title 
Usage: go run main.go https://soulminingrig.com/
alleycat:[haturatu]:~/git/go-title$ echo $?
1
alleycat:[haturatu]:~/git/go-title$ ./go-title http://https://soulminingrig.nodomain/
Error: failed to fetch URL: Get "http://https//soulminingrig.nodomain/": dial tcp: lookup https: no such host
alleycat:[haturatu]:~/git/go-title$ echo $?
2

回傳值正確地返回了。

嘗試將回傳值為0的成功模式丟棄到/dev/null

可以使用> /dev/null來丟棄標準輸出中回傳值為0的部分,讓我們試試看。

alleycat:[haturatu]:~/git/go-title$ ./go-title > /dev/null
alleycat:[haturatu]:~/git/go-title$ ./go-title http://https://soulminingrig.nodomain/ > /dev/null

在這種情況下,fmt.Println屬於標準輸出,因此無法確認錯誤訊息。
大多數正確的GNU Tools或其他執行檔通常不會將錯誤訊息輸出到標準輸出,因此錯誤訊息也必須輸出到錯誤輸出。
即使在官方文件中,也大多是使用fmt.Print進行輸出,但是
Return and handle an error
https://pkg.go.dev/errors
這只是因為在Go的世界中建立模組時,通常沒有必要特意使用標準錯誤輸出,但這不符合在偵錯時只想透過> /dev/null查看錯誤輸出的需求。
歸根結底,這裡需要像C語言一樣使用fprint來處理。
接下來,讓我們嘗試將os.Stderr作為標準錯誤輸出傳遞給fprint的第一個參數。

fmt.Print 與 fmt.Fprint 的差異

讓我們進行以下更改。
這篇文章非常容易理解。
Go 的 fmt.Print 系列函數想好好區分使用
如果這篇文章仍然讓您感到困惑,那麼您可以查閱C語言中print系列輸出的差異,或許能找到滿意的解釋。
如果printf讓您感到困惑,也可以嘗試使用awk

$ ls -la | awk '{print $9}'
$ ls -la | awk '{printf $9}'

awk中使用fprint是...。由於知識不足,我不知道...。
如果有人知道,請告訴我。

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintln(os.Stderr, "Usage: go run main.go https://soulminingrig.com/")
		os.Exit(1)
	}

	url := os.Args[1]
	title, err := fetchTitle(url)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(2)
	}

	fmt.Printf("Title: %s\n", title)
}

現在,讓我們看看結果。

alleycat:[haturatu]:~/git/go-title$ ./go-title > /dev/null
Usage: go run main.go https://soulminingrig.com/
alleycat:[haturatu]:~/git/go-title$ echo $?
1
alleycat:[haturatu]:~/git/go-title$ ./go-title http://https://soulminingrig.nodomain/ > /dev/null
Error: failed to fetch URL: Get "http://https//soulminingrig.nodomain/": dial tcp: lookup https: no such host
alleycat:[haturatu]:~/git/go-title$ echo $?
2

錯誤輸出已正確執行。

我深刻地感覺到,這類處理方式真的很容易忘記,所以必須特別注意正確的錯誤輸出。

Related Posts