Shell

Bash 函式在 Zsh 中不起作用

  • January 1, 2013

我一直在慢慢地從 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則設置為 contains b,而不是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

引用自:https://unix.stackexchange.com/questions/59883