在zsh中使用多点路径,例如 `cd ....`。

16

所有的Shell都能理解这些命令:

$ cd .
$ cd ..

而且 zsh 也会理解:

$ cd ...
$ cd ....

只要你说:

$ alias -g ...='../..'
$ alias -g ....='../../..'

现在,当我打了cd ..../<TAB>时,我该如何使它可以正确地自动补全?我记得oh-my-zsh实现了这个功能,但我现在已经停止使用它了。

如果它不仅能用于cd,而且还能执行cat ..../a/b/..../c/d | less,那将会非常赞。


什么是alias -g? - Felix Dombek
1
@FelixDombek,你有关于-g标志的答案吗? - Timo
4个回答

10
我对其他答案不满意,所以花了一些时间来得到更符合我的口味的东西。以下内容将在您按下(回车)或(制表符)时展开这些点,而不是在您输入点时展开。
function expand-dots() {
    local MATCH
    if [[ $LBUFFER =~ '(^| )\.\.\.+' ]]; then
        LBUFFER=$LBUFFER:fs%\.\.\.%../..%
    fi
}

function expand-dots-then-expand-or-complete() {
    zle expand-dots
    zle expand-or-complete
}

function expand-dots-then-accept-line() {
    zle expand-dots
    zle accept-line
}

zle -N expand-dots
zle -N expand-dots-then-expand-or-complete
zle -N expand-dots-then-accept-line
bindkey '^I' expand-dots-then-expand-or-complete
bindkey '^M' expand-dots-then-accept-line

1
你介意我将这个答案中的代码转换成一个zsh包吗? - Tarrasch
@Tarrasch,没问题,请继续。 - Paul Ruane
1
谢谢!我稍微修改了一下,让它只在...出现在行首或空格后时才应用。这样就避免了它破坏gitp4命令,但代价是不能在路径中间使用了,不过我从来没有输入过像cd a/b/..../y/z这样的东西,所以我无所谓。 - Parker Coates

7

我处理相同问题的方法是让zsh在我输入...时填充../..,这样扩展它很合理。也许适合你(或者不适合 :-P):

if is-at-least 5.0.0 && [[ ! $UID -eq 0 ]]; then                                                                                                                             
  ## http://www.zsh.org/mla/users/2010/msg00769.html                                                                                                                       
  function rationalise-dot() {                                                                                                                                             
    local MATCH # keep the regex match from leaking to the environment                                                                                                   
    if [[ $LBUFFER =~ '(^|/| |      |'$'\n''|\||;|&)\.\.$' && ! $LBUFFER = p4* ]]; then                                                                                  
        #if [[ ! $LBUFFER = p4* && $LBUFFER = *.. ]]; then                                                                                                               
        LBUFFER+=/..                                                                                                                                                     
    else                                                                                                                                                                 
        zle self-insert                                                                                                                                                  
    fi                                                                                                                                                                   
}                                                                                                                                                                        
zle -N rationalise-dot                                                                                                                                                   
bindkey . rationalise-dot                                                                                                                                                
bindkey -M isearch . self-insert                                                                                                                                         
fi

我还为...设置了一个别名,但它不是全局的。

请注意,我检查命令行是否以p4(Perforce命令行工具)开头,并在这种情况下不对其进行干扰,因为Perforce参数通常涉及文字...。 如果您不使用p4,则可以显然地删除该检查。


这是一个有用的技巧,但不完全是我要找的。 :) - Tarrasch
这是一个改进版本,也适用于使用 p4git 的别名:https://github.com/blueyed/oh-my-zsh/blob/master/functions/rationalise-dot - blueyed

5
一个不错的选择是 manydots-magic,它将 ... 扩展成 ../.. 等,但它能够智能地执行。有关更多详细信息,请参见上面的链接,简而言之:
  • 如果 Backspace 是你最后键入的内容,则可以用一个按键撤消扩展。
    • 但它不会撤消显式键入的 ../..
  • 您可以在行内使用它,例如:cd a/b/..../y/z
  • 然而,在不合适的情况下,例如:git log branch...,它不会进行扩展
  • 当扩展有可能有用时,它将进行扩展,但当您键入更多时,它将还原。例如:
    • git diff ... -> git diff ../..
    • git diff ...b -> git diff ...b(对于 git diff ...branch

2
您需要使用compinit并将_expand_alias用作补全器。以下是一个示例:
zstyle ':completion:*' completer _complete _ignored _expand_alias
autoload -Uz compinit
compinit

_complete _ignoredcompleter 的默认设置,你可以将其设置为仅 _expand_alias,但这样补全只适用于别名。

如果你的 ~/.zshrc 中已经配置了 compinit,那么你只需要将 _expand_alias 添加到 completer 的列表中即可,例如:

zstyle ':completion:*' completer _expand _complete _ignored _approximate _expand_alias

默认情况下,_expand_alias 会扩展全局和常规别名,如果您不想扩展常规别名,请设置:

zstyle ':completion:*' regular false

注意:当全局别名可用时,此方法才有效。因此,它们不会作为整个路径的一部分进行扩展,例如 a/b/..../c/d


在按照以下步骤后,我无法让 cd .../<TAB> 正常工作:(1)删除 .zshrc,(2)打开 zsh,(3)粘贴您答案中的那三行连续代码,(4)alias -g ...='../..'。但是,cd .../<TAB> 仍然无法使用 Tab 键。这在您的机器上可以正常工作吗?我是否漏掉了某个步骤? - Tarrasch
如果您在 .../ 后按 *<Tab>*,并且将 ... 定义为别名,则不起作用,因为 / 不是别名的一部分。请直接在 ... 后按 <Tab> 或者也可以定义 alias -g .../='../..'。@Tarrasch - Adaephon
我理解的是<Tab>键将.../扩展为../../,这不是我想要的。我希望它保持为.../,但会像我输入了../../ <Tab>一样弹出完成选项。我非常确定omz有这种行为... - Tarrasch
我不确定这会如何工作,因为全局别名在单词内部不会被扩展,甚至不会被识别为全局别名。这意味着,即使您将 ... 映射为上述内容,/a/b/.../c 也无法让您进入 /c - Adaephon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接