在 Day10 跟 Day11 中我們使用了 Go 提供的 os.Setenv 、 os.Unsetenv 跟 os.ExpandEnv 完成存取環境變數,因為 環境變數是系統提供的功能 ,所以系統會提供這三個 System Call
alias 也是類似的做法,但因為 alias 不是系統的功能,所以系統不會提供類似的 SetAlias
、UnsetAlias
跟 ExpandAlias
給我們用,這些都要自己實作才行,今天就要使用 Map 來實作這三個 function
為了要做到把 gst
替換成 git status
,Shell 必須自己維護一個 Alias Table 來記錄指令間的對應關係,如下圖 gst
對應到 git status
、la
對應到 ls -la
如果使用者下 ls -h
時 expandAlias
就要負責把 ls
展開成 ls -l
,所以最後會變成 ls -l -h
如果對於 Map 的語法不太熟悉的話,建議先到我去年的 Day06-Go 語法 II 了解一下 Map,才能看懂下面的實作
// 初始化一個空的 alias table
// 型別是 string 對應到 string
var aliasTable = map[string]string{}
func setAlias(key, value string) {
// 把 key 跟對應的 value 丟到 alias table 裡面去
// Ex: aliasTable["ls"] = "ls -l"
aliasTable[key] = value
}
func unsetAlias(key string) {
// 把 table 中的某個 key 刪掉
// Ex: delete(aliasTable, "ls")
delete(aliasTable, key)
}
strings.Replace
可以用來把字串 str
中的 old
字串替換成 new
字串,最後的 n 可以設定最多取代幾個
func expandAlias(input string) string {
// 把 input 的指令跟參數切開
// input = "ls -h"
// args = ["ls", "-h"]
args := strings.SplitN(input, " ", 2)
// 拿出指令部分,cmd = "ls"
cmd := args[0]
// 如果這個指令有在 alias table 裡面
// 就會得到一個 expandedCmd(展開過後的指令)
// 再用新的指令取代舊的指令
if expandedCmd, ok := aliasTable[cmd]; ok {
// cmd = "ls" -> expandedCmd = "ls -l"
// 把第一個出現的 "ls" 替換成 "ls -l" 就大功告成了
return strings.Replace(input, cmd, expandedCmd, 1)
}
// 如果指令不在 alias table 裡面
// 就什麼都不做,回傳原本的 input
return input
}
因為今天也沒有可以 Demo 的新 feature,所以寫個測試來測一下 expandAlias
有沒有正常運作
// alias_test.go
func TestExpandAlias(t *testing.T) {
// case1: 試試看 "ls -h" 有沒有被展開成 "ls -l -h"
setAlias("ls", "ls -l")
input, ans := "ls -h", "ls -l -h"
if expandAlias(input) != ans {
t.Fail()
}
// case2: 試試看 "gst" 有沒有被展開成 "git status"
setAlias("gst", "git status")
input, ans = "gst", "git status"
if expandAlias(input) != ans {
t.Fail()
}
}
看起來是沒什麼問題呢 ?
今天寫的扣也滿多的,而且 expandAlias
的邏輯有點複雜,對 Go 不太熟的人可能要花點時間看。如果有什麼問題的話歡迎在下方提問,沒問題的話就明天見囉