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

How to prevent execution of command in ZSH?

我为命令行编写了钩子:

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

function question_to_man() {
    if [[  =~ '^\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?

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

?zsh 特有的,是单个字符的通配符。这意味着如果您键入 ls? zsh 会尝试在当前目录中查找匹配的文件名(任何以 "ls" 开头的三个字母的名称)。

有两种解决方法:

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

  2. 你让 zsh 更好地处理不匹配的情况:

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

    setopt NO_NOMATCH
    setopt NO_NULL_GLOB
    

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

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

这两种方法各有利弊。 1. 可能不完全是您要找的东西,并且 2. 并非每次都能正常工作并改变您的 shell 行为。


另外(正如@chepner 在他的评论中指出的那样)preexec 运行s additionally to not 代替命令。这意味着你可能会得到 ls 的帮助,但 zsh 仍然会尝试 运行 ls? 甚至 lsx (或其他匹配姓名)。

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

If no external command is found but a function command_not_found_handler exists the shell executes this function with all command line arguments. The function should return status zero if it successfully handled the command, or non-zero status if it failed. In the latter case the standard handling is applied: ‘command not found’ is printed to standard error and the shell exits with status 127. Note that the handler is executed in a subshell forked to execute an external command, hence changes to directories, shell parameters, etc. have no effect on the main shell.

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

command_not_found_handler () {
    if [[  =~ '\?$' ]]; 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) 而不是 运行s man 用于当前命令。

与上述相比,使用 run-help 的主要优点是:

  1. 您可以在键入较长命令时随时调用它,只要命令名称完整即可。
  2. 离开联机帮助页后,命令仍未更改,因此您可以继续在上面书写。

您甚至可以将其绑定到 Alt+? 以使其更相似:bindkey '^[?' run-help