假设我的文件中有以下条目/etc/hosts
192.168.1.10 server1.mydomain.com
我SERVER-FILES
在当前目录中有一个目录。我想要scp
某个目录SERVER-FILES
。我键入SE
并使用自动完成功能来完成目录名称:
$ scp -rp SE<TAB>
这个完成应该是完全明确的。但是 zsh 自动完成试图变得太聪明,并且对待主机名不区分大小写,因此尝试匹配SE
主机名:
$ scp -rp SE<TAB>
SERVER-FILES/
server1.mydomain.com
我如何禁用这个恼人的功能,其中 zsh 尝试匹配不区分大小写的主机名,从而完成SE<TAB> to
server1.mydomain.com` ?
更新:
根据 的建议@zeppelin
,我更改了 ssh 完成文件中的以下行Unix/_ssh
:
- compadd -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' "$@" $config_hosts
+ compadd "$@" $config_hosts
但这没有帮助。它完全没有效果。
而且我不明白 的答案@Tomasz Pala
。我的 zsh 补全不区分大小写。
请有人告诉我需要改变什么/usr/share/zsh/functions/Completion/Unix/_foo
才能改变这种行为。
更新2
我终于缩小了问题范围,并找出了为什么解决方案@Tomasz Pala
对他有效,但对我无效:
Unix/_hosts
当我在新设置的计算机/用户帐户上更改文件时,该解决方案有效。
scp -r SE<TAB>
上面的命令忽略server1.mydomain.com
in /etc/hosts
,并且只提供本地目录SERVER-FILES
来完成。
但这对我现有的用户帐户不起作用,因为我
server.mydomain.com
的~/.ssh/config
.当我删除该条目时,一切都会按预期进行。
但即使在我目前的情况下,我怎样才能让这个黑客工作~/.ssh/config
呢?
答案1
第二个答案试图解释你需要做两件事:
1_ 确保您的一般匹配规则不区分大小写 ( matcher-list
) - 从更新的问题来看,它不是,
2_改变Unix/(类型/)_主机(实际位置可能有所不同,但是不是这Unix/_ssh- 这个处理~/.ssh/config
主机,见下文)最后两行:
_wanted hosts expl host \
compadd -M 'm:{a-z}={A-Z} r:|.=* r:|=*' -a "$@" - _hosts
所有这些都已经在我的回答中进行了总结,因此只需尝试执行此操作,而无需阅读之前的所有基本原理。另外,由于您的全局配置不区分大小写,@zeppelin 的答案也应该有效,尽管它不使用 $fpath 并且还删除了主机的小->大写字母匹配。
我确实使用更新中的设置对此进行了测试,它按预期工作。
更新:请记住zsh
保持其功能的加载,因此修改后_hosts
您需要通过重新登录来重新加载它,或者:
unfunction _hosts
autoload -Uz _hosts
另请记住,可以以( ) 的形式zsh
“编译”脚本,如果此类文件存在并且比源文件更新,则将使用该文件。zwc
zcompile [file]
广告。更新2:
处理~/.ssh/config
定义的主机实际上与_hosts
- 取决于您的zsh版本在任一Unix/(命令/)_ssh或者Unix/(类型/)_ssh_hosts改变
compadd -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' "$@" $config_hosts
线路到
compadd -M 'm:{a-z}={A-Z} r:|.=* r:|=*' "$@" $config_hosts
答案2
我相信您正在编辑错误的行。
AFAIK配置主机在Unix/_ssh指的是您的主机条目〜./ssh/config, 不是/etc/主机。
完成规则为/etc/主机定义得更早一些,在以下块中:
# If users-hosts matches, we shouldn't complete anything else.
if [[ "$IPREFIX" == *@ ]]; then
_combination -s '[:@]' my-accounts users-hosts "users=${IPREFIX/@}" hosts "$@" && return
else
_combination -s '[:@]' my-accounts users-hosts \
${opt_args[-l]:+"users=${opt_args[-l]:q}"} hosts "$@" && return
fi
但这反过来又重复使用了主机样式定义于Unix/_hosts
所以如果你编辑compad定义在最后Unix/_hosts像这样的文件:
#_wanted hosts expl host \
# compadd -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' -a "$@" - _hosts
_wanted hosts expl host \
compadd -a "$@" - _hosts
你应该得到你想要的行为。
聚苯乙烯
请注意,编辑系统范围的完成文件通常不是一个很好的做法,因此您可能只想重新定义主机相反,在您的本地 ZSH 配置中,例如通过将类似的函数添加到您的 ~./zsh 中:
_hosts() { compadd $(getent hosts | tr -s ' ' '\t' | cut -f2) }
答案3
这个问题有两个方面。
- 不区分大小写的全局 zsh 补全。它很受欢迎,但恕我直言,双向进行是没有意义的。由于我通常不输入大写字母(或错误地输入),因此我们将所有大写字母视为有意输入:
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
mkdir DOC drone
ls D[tab] => DOC
ls d[tab] => drone DOC
ls Dr[tab] => [empty]
匹配器列表的问题在于很早就被调用,并且不能根据每个命令/参数进行替换。
并且由于匹配器列表重复值被连接起来(引用自文档:“该选项可以多次给出。在这种情况下,给出的所有匹配规范都用空格连接起来,形成要使用的规范字符串"),您以后不能覆盖它,例如
compdef '_hosts -M m:' foo
不会撤销 _hosts 文件中先前的定义。因此,如果您的全局配置中有{a-zA-Z}={A-Za-z}
,您将无法清除它,即使使用matcher
,例如:
zstyle ':completion:*:scp:*:*' matcher "m:{A-Z}={A-Z}"
没有帮助。抱歉,您需要在全球范围内开始解决此问题。权衡解决方案可能是全局两次传递:
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' 'm:{A-Z}={a-z}'
ls d[tab] => DOC drone
ls D[tab] => DOC
ls Dr[tab] => drone
- 问题的第二部分是函数定义的匹配器列表。如上所述,这是不可清除的,因此您需要通过更改 zsh 提供的文件来处理这个问题,就像 @zeppelin 答案中已经涵盖的那样:
_主机:
[....]
_wanted hosts expl host \
compadd -M 'm:{a-z}={A-Z} r:|.=* r:|=*' -a "$@" - _hosts
现在,使用系统范围的匹配器列表 'm:{az}={AZ}':
scp d[tab] => /directory/ DOC drone /remote host name/ docker /user/ daemon
scp D[tab] => DOC
所提出的更改原始完成文件的解决方案不是正确的方法,就像@zeppelin 指出的那样......但创建自己的主机获取功能也有缺陷。不幸的是,正确的解决方案取决于 zsh 开发人员,他们可能会添加匹配器清除选项、定义一些新样式或简单地修复完成函数以不忽略大小写,只需遵循全局设置即可。
然而,即使存在这个问题,也有一个很好且干净的解决方案 - 而不是修改(可能提供了一些发行版包) /usr/share/zsh/... 人们可以将固定副本放在 /etc/zsh/functions 中,然后简单地告诉 zsh使用此位置:fpath=(/etc/zsh/functions $fpath)
.这样可以避免系统更新后覆盖文件。
总结
- 更改您的全局匹配器列表:
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' 'm:{A-Z}={a-z}'
- 将修改后的 _hosts 文件放在某个 /etc/zsh 或 $HOME 位置:
fpath=(/etc/zsh/functions $fpath)
/etc/zsh/functions/_hosts:
[....]
_wanted hosts expl host \
compadd -M 'm:{a-z}={A-Z} r:|.=* r:|=*' -a "$@" - _hosts
答案4
您无需更改任何完成功能即可获得所需的内容。只需将其添加到您的~/.zshrc
文件中:
# $PREFIX is the part of the current word that's to the left of the cursor.
# $SUFFIX is the part of the current word that's to the right of the cursor.
# Let's ignore all host completions that don't explicitly match what we've typed,
# but allow for additional characters at the cursor position and at the end of what
# we've typed. This makes the matching case sensitive.
# `(b)` escapes characters that are significant to globbing.
zstyle -e ':completion:*:hosts' ignored-patterns 'reply=(
"^(${(b)PREFIX}*${(b)SUFFIX}*)"
)'
# But if that would lead to no results, then offer the ignored completions anyway.
zstyle ':completion:*' completer _expand _complete _ignored
我已经使用您发布的配置和测试用例对此进行了测试,并且它有效。
文档: