Bash 功能在 Zsh 中不起作用

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 下工作?

答案1

  • 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 会触发错误。添加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

答案2

zsh 的 typeset(local) 命令无法使用其语法定义数组。您可以创建数组,但不能同时在一个命令中设置所有值。

function n() {                                                         
local arg files; for arg; do files+=( ~/.notes/$arg ); done
vim ${files[@]}
}

是修复它的一种方法。

相关内容