Bash 功能在 Zsh 中不起作用

jas*_*yan 7 shell zsh function

我一直在慢慢地从 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
Run Code Online (Sandbox Code Playgroud)

我的来源.zshrc是这样的:

autoload bashcompinit
bashcompinit
# source zshrc functions file
source "$HOME/.config/zsh/functions"
Run Code Online (Sandbox Code Playgroud)

nls按预期工作,但既不是n也没有Tab完成工作。

我读到man zshcompsys它说:

bashcompinit 函数提供了与 bash 的可编程完成系统的兼容性。运行时,它将定义与具有相同名称的 bash 内置函数相对应的函数 compgen 和 complete。然后就可以使用为 bash 编写的完成规范和函数。

然而,当我尝试 Tab完成时,没有任何反应,当我输入时n notename,Vim/home在文件浏览器模式下打开我的- 不是预期的行为。

定义的所有其他函数都运行良好。如何将这些功能迁移到 Zsh 下工作?

Gil*_*il' 6

  • local是内置函数,而不是关键字,因此local files=(…)不被解析为数组赋值,而是解析为字符串赋值。将赋值与声明分开编写。(已被 llua 发现,但请注意,您需要初始化files为空数组或使用 声明变量typeset -a,否则数组以虚假空元素开头。)
  • Zsh 数组从 1 开始编号,而不是像 bash 和 ksh 那样从 0 开始,因此${files[0]}必须写成$files[1]. 或者,告诉 zsh 以与 ksh 和 bash 更兼容的方式运行:放在emulate -L ksh函数的开头。
  • 除非你走这emulate条路线,否则你的_notes函数将打印zsh: no matches found: foo*如果没有完成foo,因为默认情况下不匹配的 glob 会触发错误。添加glob限定符 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
Run Code Online (Sandbox Code Playgroud)

如果要将代码移植到 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
Run Code Online (Sandbox Code Playgroud)

  • “Zsh 数组是从 1 开始编号的,而不是像 bash 和 ksh 中那样从 0 开始编号,因此 ${files[0]} 必须写成 $files[1]” 哦,太恐怖了;( (2认同)