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

3

我为命令行编写了一个钩子:

# 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?

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


preexec 不会改变给定的命令;它只是在运行给定命令之前运行额外的代码。 - chepner
你不能只是使用一个“别名”吗?但是你想要实现什么? - Alex
1
ls?只是一个例子(systemd?mmap?malloc?- 这些也应该可以),alias在这里不合适。我经常使用IPython,在表达式<item>?中生成有关<item>的帮助信息。我想在我的ZSH终端中使用相同的逻辑。 - hxwalker
1个回答

5

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

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

  1. You can make "?" "unspecial" by quoting it: ls\?, 'ls?' or "ls?".

  2. You make zsh handle the cases where it does not match better:

    The default behaviour if no match can be found is to print an error. This can be changed by disabling the NOMATCH option (also NULL_GLOB must not be set):

    setopt NO_NOMATCH
    setopt NO_NULL_GLOB
    

    This will leave the word untouched, if there is no matching file.

    Caution: In the (maybe unlikely) case that there is a file with a matching name, zsh will try to execute a command with the name of the first matching file. That is if there is a file named "lsx", then ls? will be replaced by lsx and zsh will try to run it. This may or may not fail, but will most likely not be the desired effect.

两种方法各有优缺点。第一种可能不完全符合您的要求,而第二种并不总是有效,并且会改变您的shell行为。
此外(正如@chepner在他的评论中指出的),preexec 除了而不是代替命令运行。这意味着你可能会得到ls的帮助,但zsh仍然会尝试运行ls?或甚至lsx(或另一个匹配的名称)。
为了避免这种情况,我建议定义一个command_not_found_handler函数来代替preexec。从zsh手册中可以看到:
如果没有找到外部命令但存在一个command_not_found_handler函数,则 shell 将使用所有命令行参数执行此函数。如果该函数成功处理了命令,则应返回零状态;如果失败,则返回非零状态。在后一种情况下,将应用标准处理:将“未找到命令”打印到标准错误,并以状态127退出 shell。请注意,处理程序在子 shell 中执行,该子 shell 被 fork 用于执行外部命令,因此对目录、shell 参数等的更改不会影响主 shell。
所以这样做就可以解决问题了:
command_not_found_handler () {
    if [[ $1 =~ '\?$' ]]; then
        man ${1%\?}
        return 0
    else
        return 1
    fi
}

如果你有很多匹配的文件名,但很少输错命令(通常是“找不到命令”的原因),那么你可能想考虑使用这个替代方法:
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. 离开手册页后,命令仍然保持不变,因此您可以继续编写它。

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


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