Shell
Bash 函式在 Zsh 中不起作用
我一直在慢慢地從 Bash 遷移到 Zsh,並且已經到了我所遷移的所有東西都執行良好的地步,除了一個例外。
我有幾個函式,我
.bashrc
每天使用幾十次,其中兩個在 Zsh 下不起作用。這三個功能包括一個基本的筆記功能。他們目前在
.config/zsh/functions
:function n() { local arg files=(); for arg; do files+=( ~/".notes/$arg" ); done ${EDITOR:-vi} "${files[@]}" } function nls() { tree -CR --noreport $HOME/.notes | awk '{ if (NF==1) print $1; else if (NF==2) print $2; else if (NF==3) printf " %s\n", $3 }' } # TAB completion for notes function _notes() { local files=($HOME/.notes/**/"$2"*) [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]##~/.notes/}" ) } complete -o default -F _notes n
我的來源
.zshrc
是這樣的:autoload bashcompinit bashcompinit # source zshrc functions file source "$HOME/.config/zsh/functions"
nls
按預期工作,但既不工作n
也不Tab
完成工作。我讀到
man zshcompsys
它說:函式 bashcompinit 提供與 bash 的可程式完成系統的兼容性。執行時,它將定義與具有相同名稱的 bash 內置函式相對應的函式 compgen 和 complete。然後可以使用為 bash 編寫的完成規範和函式。
但是,當我嘗試
Tab
完成時,什麼也沒有發生,當我進入時n notename
,Vim會/home
以文件瀏覽器模式打開我的 - 這不是預期的行為。定義的所有其他功能都執行良好。如何遷移這些功能以在 Zsh 下工作?
local
是內置函式,而不是關鍵字,因此local files=(…)
不會被解析為數組賦值,而是作為字元串賦值。將賦值與聲明分開編寫。(已經被 llua 找到了,但請注意,您需要初始化files
為空數組或使用 聲明變數typeset -a
,否則該數組以虛假的空元素開頭。)- Zsh 數組從 1 開始編號,而不是像 bash 和 ksh 中那樣從 0 開始編號,因此
${files[0]}
必須寫入$files[1]
. 或者,告訴 zsh 以一種與 ksh 和 bash 更兼容的方式執行:放在emulate -L ksh
函式的開頭。- 除非你走這
emulate
條路,否則如果沒有完成,你的_notes
函式將列印出來,因為預設情況下不匹配的 glob 會觸發錯誤。添加glob 限定符,如果沒有匹配則得到一個空數組,並測試該數組是否為空。zsh: no matches found: foo*``foo
N
- 您的函式中還有另一個錯誤
_notes
會影響子目錄中的註釋:您必須去掉前綴直到完成,以便如果 eg~/notes/foo/bar
存在並且您鍵入n b<TAB>
,COMPREPLY
則設置為 containsb
,而不是foo/b
。如果要保留 bash 和 zsh 均可讀取的文件:
type emulate >/dev/null 2>/dev/null || alias emulate=true function n() { emulate -L ksh local arg; typeset -a files for arg; do files+=( ~/".notes/$arg" ); done ${EDITOR:-vi} "${files[@]}" } function nls() { tree -CR --noreport $HOME/.notes | awk '{ if (NF==1) print $1; else if (NF==2) print $2; else if (NF==3) printf " %s\n", $3 }' } # TAB completion for notes function _notes() { emulate -L ksh local x files files=($HOME/.notes/**/"$2"*) [[ -e ${files[0]} ]] || return 1 COMPREPLY=() for x in "${files[@]}"; do COMPREPLY+=("$2${x#$HOME/.notes*/$2}") done } complete -o default -F _notes n
如果要將程式碼移植到 zsh:
function n() { local files files=(${@/#/~/.notes/}) ${EDITOR:-vi} $files } function nls() { tree -CR --noreport $HOME/.notes | awk '{ if (NF==1) print $1; else if (NF==2) print $2; else if (NF==3) printf " %s\n", $3 }' } # TAB completion for notes function _notes() { setopt local_options bare_glob_qual local files files=(~/.notes/**/$2*(N)) ((#files)) && COMPREPLY=($2${^files##~/.notes*/$2}) } complete -o default -F _notes n