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

hxw*_*ker 3 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
Run Code Online (Sandbox Code Playgroud)

但当我这样做时:

> ls?
Run Code Online (Sandbox Code Playgroud)

退出后man我得到:

> zsh: no matches found: ls?
Run Code Online (Sandbox Code Playgroud)

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

Ada*_*hon 5

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

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

  1. 你(们)能做到 "?" "unspecial"引述它:ls\?,'ls?'"ls?".

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

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

    setopt NO_NOMATCH
    setopt NO_NULL_GLOB
    
    Run Code Online (Sandbox Code Playgroud)

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

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

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


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

为避免这种情况,我建议定义一个command_not_found_handler函数而不是preexec.从zsh手册:

如果未找到外部命令但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
}
Run Code Online (Sandbox Code Playgroud)

如果你有很多匹配的文件名但很少错误输入命令("找不到命令"错误的常见原因)你可能想要考虑使用它:

command_not_found_handler () {
    man ${1%?}
}
Run Code Online (Sandbox Code Playgroud)

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

注意:这将干扰其他工具command_not_found_handler,例如使用command-not-foundUbuntu中的工具(如果为zsh启用).


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

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

  1. 只要命令名称完整,您可以在键入较长命令时随时调用它.
  2. 离开联机帮助页后,命令仍然保持不变,因此您可以继续在其上书写.

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