前面經過六天的基礎觀念建立,第七天我們來實際寫一些東西。go因為是編譯式語言,顧名思義,需要編譯過後才能執行,也因此無法提供像是ruby有的命令行環境(command line)。儘管如此,go還是可以做某種程度上的命令行互動,就是透過今天要講的命令行參數。透過在命令行加上參數,傳遞到go當中。
為了接受命令行的參數,我們需要使用os
包,在程式一開頭引入進來:
package main
import (
"fmt"
"os"
)
func main() {
var s, sep string
for i := 1; i < len(os.Args); i++ {
s += sep + os.Args[i]
sep = ","
}
fmt.Println(s)
}
完成後,將檔案命名為cammand_line.go
,接著我們就可測試效果:
$ go run cammand_line.go 1 2 3
1,2,3
$ go run cammand_line.go hello
hello
基本上就是把得到的參數印出來,如果參數不只一個,在中間加上逗號分隔。我們觀察一下for迴圈內,os.Args[i]
透過索引i
取得命令行參數,參數由1開始,0的位置是命令本身。我在第五天的時候有介紹過不同的For迴圈,我們來試試看用另外一種寫法:
for _, arg := range os.Args[1:] {
s += sep + arg
sep = ","
}
基本上可以跟原本的程式得到相同的結果。我們可以觀察到range
會回傳兩個變數,這邊的範例因為沒有用到一個,所以使用_
(blank identifier)來做拋棄,我們也可以把他印出來看看,其實他就是索引:
func main() {
for i, arg := range os.Args[1:] {
fmt.Println(i, arg)
}
}
我把程式改寫為這樣,然後試著輸入多個參數:
$ go run cammand_line.go aa bb cc
0 aa
1 bb
2 cc
因為把印出放在迴圈內,所以作出換行的效果。如果只是想印出變數,不太在意印出的效果,也可以簡單寫為一行:
func main() {
fmt.Println(os.Args[1:])
}
會得到去掉命令行本身的所有參數,放在一個Array裡面:
$ go run cammand_line.go 1 2 3
[1 2 3]
既然能得到參數內容,代表我們可以做簡單的互動程式,比方說輸入網址,得到該網址的標題(Title)。前面有提過go先天對網路服務的支援很齊全,我們這次使用io/ioutil
與net/http
,範例如下:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
for _, url := range os.Args[1:] {
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
fmt.Printf("%s", b)
}
}
將檔案命名為parse.go
,我們就可以使用:
$ go run parse.go http://google.com
// 返回整包html檔案
接下來我們只要改寫一下,從整包html當中找出title,然後印出來。
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
func main() {
for _, url := range os.Args[1:] {
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
s := string(b)
first_index := strings.Index(s, "<title>") + 7
last_index := strings.Index(s, "</title>")
fmt.Println(s[first_index:last_index])
}
}
從resp.Body
經過ReadAll得到的是byte[]
,需要先轉為字串,接著透過字串的index去把網站標題的索引位置找出來。在html開頭的地方需要加上<title>
本身的長度,不然這段也會跟著被印出來。取得頭尾後,再透過之前講過的字串方法取得字串中的特定值,我們來看一下實際操作:
$ go run parse.go http://google.com
Google
$ go run parse.go https://ithelp.ithome.com.tw/users/20103651
iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天
恩,非常完美。當然這段程式碼或許有可以改進的地方,第七天就以標題做個結尾吧!基本上能爬出標題,網頁上的靜態內容都可以取得,動態的話可能要另外下一番工夫。
我把程式放上github了,有需要的朋友可以直接pull下來玩玩看。