Goで標準エラー出力に回すとき
4 min read
こんにちは、無能です。
割とここの部分で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
で扱う必要がある。
次にfprintの第一引数に標準エラー出力としてのos.Stderr
を与えてみよう。
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
正しくエラー出力が行われました。
本当にこの手の扱いってかなり頭から飛びやすいので、特に正しいエラー出力には注意して触らないとなあ…とつくづく感じました。