我有一个函数可以在不同的类别中运行单个命令。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
的调用更改为。)cd
builtin cd
答案1
这是我到目前为止所拥有的。它并不完美,但它确实:
- 相对于目标目录完成;
- 如果目标目录不存在,则静默失败;
- 跳过
chpwd
并且chpwd_functions
我认为正确地恢复它们; - 如果取消完成,则恢复当前目录。
它看起来复杂且脆弱,所以我对极端情况并不完全有信心。已知的问题:
- 完成并不总是添加后缀字符(例如空格或
/
目录。例如cd1 / echo /bi
Tabinsertsn/
但cd1 / echo bi
Tab 仅插入n
(而echo bi
Tabinsert 则如此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 "$@"