完成完全相对于不同目录的命令

完成完全相对于不同目录的命令

我有一个函数可以在不同的类别中运行单个命令。cd1 SOMEDIR MYCOMMAND ARG…相当于(cd SOMEDIR && MYCOMMAND ARG…),有两个优点。它的输入稍微容易一些(当你是一个命令行迷时,每个按键都很重要),并且(大多数情况下),它让我可以完成相对于SOMEDIR.

也就是说,它让我定义 的完成函数cd1我该如何安排来完成相对SOMEDIR

如果不进行修改,以下方法将不起作用:

  • _files使用命令参数的修改参数进行临时调用在这里是不好的(与那里)因为我希望应用所有常见的上下文相关完成。
  • (cd $words[2] && shift 2 words && ((CURRENT-=2)) && _normal)如果完成在子 shell 中工作,那么会做正确的事情,但事实并非如此。
  • cd $words[2] && shift 2 words && ((CURRENT-=2)) && _normal; cd $OLDPWD在正常情况下可以工作,但在某些情况下会中断并让我陷入困境SOMEDIR,例如如果我按Ctrl+C取消需要很长时间的完成。

这是 的定义cd1。它支持别名并相对于目标目录扩展通配符(另一方面,命令替换等仍将在原始目录中运行)。

cd1_glob () {
  if (($# <= 1)); then
    cd -- "$1"
  elif (($+aliases[$2])); then
    ( cd -- $1 && eval $2 '$~@[3,$#]' )
  else
    ( cd -- $1 && $~@[2,$#] )
  fi
}
alias cd1='noglob cd1_glob'

(我不调用此函数。如果您这样做,请将函数内部cd的调用更改为。)cdbuiltin cd

答案1

这是我到目前为止所拥有的。它并不完美,但它确实:

  • 相对于目标目录完成;
  • 如果目标目录不存在,则静默失败;
  • 跳过chpwd并且chpwd_functions我认为正确地恢复它们;
  • 如果取消完成,则恢复当前目录。

它看起来复杂且脆弱,所以我对极端情况并不完全有信心。已知的问题:

  • 完成并不总是添加后缀字符(例如空格或/目录。例如cd1 / echo /biTabinsertsn/cd1 / echo biTab 仅插入n(而echo biTabinsert 则如此n/)。
  • 如果在完成操作期间对原始目录进行了重命名,则 shell 将返回到具有旧名称的新目录,而不是具有新名称的旧目录。
#compdef cd1 cd1_glob
# cd for one command

_cd1_in () {
  setopt local_options local_traps
  # Cleanup:
  # * Restore the old directory. We do this only if cd succeeded beause
  #   the restoration can do the wrong thing in corner cases (renamed
  #   directory).
  # * Restore the chpwd hook function and the hook array.
  trap 'if ((_cd1_cd_succeeded)); then cd $OLDPWD; fi
        if [[ -n $_cd1_chpwd_function ]]; then
          functions[chpwd]=$_cd1_chpwd_function
        fi
       ' EXIT INT
  builtin cd $words[2] 2>/dev/null || { _cd1_cd_succeeded=0; return 1; }
  shift 2 words; ((CURRENT -= 2))
  _normal
}

_cd1 () {
  if ((CURRENT > 2)); then
    setopt local_options no_auto_pushd unset
    local _cd1_cd_succeeded=1
    # Save the current directory and the chpwd hook. They will be restored
    # by the exit trap in _cd1_in.
    local _cd1_chpwd_function=$functions[chpwd]
    # Save the current directory in $OLDPWD. _cd_in will call cd, which would
    # generally call cd
    local OLDPWD=$PWD
    # Turn off the chpwd hook function and the associated hook array.
    local chpwd_functions=
    unset -f chpwd 2>/dev/null
    # Call a separate function to do the work. Its exit trap will take care
    # of cleanup. The reason to have a separate function is so that the
    # local variables defined here can be used in the exit trap.
    _cd1_in
  else
    _dirs
  fi
}

_cd1 "$@"

相关内容