
我正在使用zsh
shell 並在我的 中包含以下程式碼.zshrc
:
fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc
autoload -Uz compinit
compinit
第一行新增~/.zsh/completion
儲存在環境變數中的陣列的路徑fpath
。
第二_vc
行為名為 的自訂 shell 函數載入名為 的完成函數vc
。的目的vc
只是編輯特定資料夾 ( ~/.cheat
) 內的文件。_vc
在文件中定義~/.zsh/completion/_vc
。
最後兩行啟用完成系統。
這是我的完成功能的程式碼_vc
:
#compdef vc
declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0
我是從這個複製的地址,並根據我的需要進行了調整。
只要目錄中~/.cheat
沒有名稱包含單引號的文件,補全就會起作用。但如果有一個,例如foo'bar
,則完成將失敗並顯示以下錯誤訊息:
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
我找到了一個解決方案,將行中的雙引號替換cheatsheets="$(ls ~/.cheat)"
為單引號cheatsheets='$(ls ~/.cheat)'
。
現在,當我在命令Tab
後點擊時vc
, zsh 會建議 內的文件~/.cheat
,包括foo\'bar
(zsh 似乎已自動轉義單引號)。
但是,我不明白它是如何或為什麼起作用的。使用單引號,變數cheatsheets
將包含一個文字字串。所以$(...)
命令替換不應該被擴充。例如,如果我執行以下命令:
myvar='$(ls)'
echo $myvar → outputs `$(ls)` literally
那為什麼要'$(ls ~/.cheat)'
在裡面進行擴充呢_arguments "1:cheatsheets:(${cheatsheets})" && return 0
?為什麼它會自動轉義裡面的單引號foo'bar
?
答案1
如果我們堅持以錯誤的方式做事™
#compdef vc
declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0
哎呀!如果檔案名稱包含換行符,這當然會中斷,因為(f)
它們會被分割。解析ls
無論如何都是個壞主意; ZSH 可以將 glob 檔案直接放入陣列中:
% mkdir ~/.lojban
% touch ~/.lojban/{go\'i,na\ go\'i}
% words=(~/.lojban/*)
% print -l ${words:t}
go'i
na go'i
% words=(~/.lojban/*(On))
% print -l ${words:t}
na go'i
go'i
%
但我們可能不需要本地數組;_files
可以在 glob 上完成:
#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0
這將返回完全限定的檔案路徑;如果搜尋目錄中只需要裸檔名,我們可以使用:
#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0