如何防止在ZSH中执行命令?

时间:2015-05-04 11:14:23

标签: linux zsh

我为命令行编写了钩子:

# Transforms command 'ls?' to 'man ls'

function question_to_man() {
    if [[ $2 =~ '^\w+\?$' ]]; then 
        man ${2[0,-2]}
    fi
}

autoload -Uz add-zsh-hook

add-zsh-hook preexec question_to_man

但是当我这样做时:

> ls?

退出man后,我得到:

> zsh: no matches found: ls?

如何摆脱有关错误命令的消息?

1 个答案:

答案 0 :(得分:5)

?对于 zsh 是特殊的,并且是单个字符的通配符。这意味着,如果您键入ls? zsh ,尝试在当前目录中找到匹配的文件名(任何以&#34开头的三个字母名称; ls")。

有两种方法可以解决这个问题:

  1. 你可以制作"?" " unspecial"引用它:ls\?'ls?'"ls?"

  2. zsh 处理不匹配的情况:

    如果找不到匹配项,则默认行为是打印错误。这可以通过禁用NOMATCH选项进行更改(同时也不能设置NULL_GLOB):

    setopt NO_NOMATCH
    setopt NO_NULL_GLOB
    

    如果没有匹配的文件,这将使单词保持不变。

    警告:在(可能不太可能)存在具有匹配名称的文件的情况下, zsh 将尝试执行具有第一个匹配名称的命令文件。也就是说,如果有一个名为" lsx"的文件,那么ls?将被lsx替换,zsh将尝试运行它。这可能会或可能不会失败,但很可能不会产生预期的效果。

  3. 这两种方法都有其优点和缺点。 1.可能不是你正在寻找的东西,2。每次都不起作用,也不会改变你的炮弹行为。

    另外(正如@chepner在他的评论中指出的那样)preexec另外运行而不是命令。这意味着您可以获得ls的帮助,但 zsh 仍会尝试运行ls?甚至lsx(或其他匹配的名称)。

    为避免这种情况,我建议您定义一个command_not_found_handler函数而不是preexec。来自zsh manual

      

    如果未找到外部命令但存在函数command_not_found_handler,则shell将使用所有命令行参数执行此函数。如果成功处理命令,该函数应返回状态零,如果失败,则返回非零状态。在后一种情况下,应用标准处理:'command not found'打印到标准错误,shell退出状态127.注意,处理程序在子shell中执行,分叉执行外部命令,因此更改为目录,shell参数等对主壳没有影响。

    所以这应该可以解决问题:

    command_not_found_handler () {
        if [[ $1 =~ '\?$' ]]; then
            man ${1%\?}
            return 0
        else
            return 1
        fi
    }
    

    如果您有很多匹配的文件名但很少会错误输入命令(通常的原因" Command not found"错误),您可能需要考虑使用它:

    command_not_found_handler () {
        man ${1%?}
    }
    

    这不会检查"?"最后,但只是删除任何最后一个字符(注意缺少" \"在${1%?}中)并尝试在其余字符上运行man。因此,即使文件名匹配,也会运行man,除非确实存在与匹配文件同名的命令。

    注意:这会干扰使用command_not_found_handler的其他工具,例如来自Ubuntu的command-not-found工具(如果为zsh启用)。

    总而言之, zsh 有一个名为run-help的小部件,可以绑定到一个键(在Emacs模式下,它默认绑定到 Alt + H )而不是为当前命令运行man

    在上面使用run-help的主要优点是:

    1. 只要命令名称完整,您就可以在输入更长的命令时随时调用它。
    2. 离开联机帮助页后,命令仍未更改,因此您可以继续在该页面上书写。
    3. 您甚至可以将其绑定到 Alt + 以使其更相似:bindkey '^[?' run-help