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

时间:2020-12-21 15:51:57

I wrote hook for command line:

我为命令行编写了钩子:

# 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

But when I do:

但当我这样做时:

> ls?

After exiting from man I get:

退出男人后,我得到:

> zsh: no matches found: ls?

How can I get rid of from message about wrong command?

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

1 个解决方案

#1


? is special to zsh and is the wildcard for a single character. That means that if you type ls? zsh tries find matching file names in the current directory (any three letter name starting with "ls").

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

There are two ways to work around that:

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

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

    你(们)能做到 ”?”引用它是“非特殊的”:ls \?,'ls?'还是“ls?”。

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

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

    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):

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

    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.

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

Both methods have their pro and cons. 1. is probably not exactly what you are looking for and 2. does not work every time as well as changes your shells behaviour.

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


Also (as @chepner noted in his comment) preexec runs additionally to not instead of a command. That means you may get the help for ls but zsh will still try to run ls? or even lsx (or another matching name).

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

To avoid that, I would suggest defining a command_not_found_handler function instead of preexec. From the zsh manual:

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

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

So this should do the trick:

所以这应该做的伎俩:

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

If you have a lot of matching file names but seldomly misstype commands (the usual reason for "Command not found" errors) you might want to consider using this instead:

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

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

This does not check for "?" at the end, but just cuts away any last character (note the missing "\" in ${1%?}) and tries to run man on the rest. So even if a file name matches, man will be run unless there is indeed a command with the same name as the matched file.

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

Note: This will interfere with other tools using command_not_found_handler for example the command-not-found tool from Ubuntu (if enabled for zsh).

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


That all being said, zsh has a widget called run-help which can be bound to a key (in Emacs mode it is by default bound to Alt+H) and than runs man for the current command.

尽管如此,zsh有一个名为run-help的小部件,它可以绑定到一个密钥(在Emacs模式下,它默认绑定到Alt + H),而不是为当前命令运行man。

The main advantages of using run-help over the above are:

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

  1. You can call it any time while typing a longer command, as long as the command name is complete.
  2. 只要命令名称完整,您可以在键入较长命令时随时调用它。

  3. After you leave the manpage, the command is still there unchanged, so you can continue writing on it.
  4. 离开联机帮助页后,命令仍然保持不变,因此您可以继续在其上书写。

You can even bind it to Alt+? to make it more similar: bindkey '^[?' run-help

你甚至可以将它绑定到Alt +?使其更相似:bindkey'^ [?'运行帮助

#1


? is special to zsh and is the wildcard for a single character. That means that if you type ls? zsh tries find matching file names in the current directory (any three letter name starting with "ls").

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

There are two ways to work around that:

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

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

    你(们)能做到 ”?”引用它是“非特殊的”:ls \?,'ls?'还是“ls?”。

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

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

    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):

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

    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.

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

Both methods have their pro and cons. 1. is probably not exactly what you are looking for and 2. does not work every time as well as changes your shells behaviour.

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


Also (as @chepner noted in his comment) preexec runs additionally to not instead of a command. That means you may get the help for ls but zsh will still try to run ls? or even lsx (or another matching name).

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

To avoid that, I would suggest defining a command_not_found_handler function instead of preexec. From the zsh manual:

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

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

So this should do the trick:

所以这应该做的伎俩:

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

If you have a lot of matching file names but seldomly misstype commands (the usual reason for "Command not found" errors) you might want to consider using this instead:

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

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

This does not check for "?" at the end, but just cuts away any last character (note the missing "\" in ${1%?}) and tries to run man on the rest. So even if a file name matches, man will be run unless there is indeed a command with the same name as the matched file.

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

Note: This will interfere with other tools using command_not_found_handler for example the command-not-found tool from Ubuntu (if enabled for zsh).

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


That all being said, zsh has a widget called run-help which can be bound to a key (in Emacs mode it is by default bound to Alt+H) and than runs man for the current command.

尽管如此,zsh有一个名为run-help的小部件,它可以绑定到一个密钥(在Emacs模式下,它默认绑定到Alt + H),而不是为当前命令运行man。

The main advantages of using run-help over the above are:

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

  1. You can call it any time while typing a longer command, as long as the command name is complete.
  2. 只要命令名称完整,您可以在键入较长命令时随时调用它。

  3. After you leave the manpage, the command is still there unchanged, so you can continue writing on it.
  4. 离开联机帮助页后,命令仍然保持不变,因此您可以继续在其上书写。

You can even bind it to Alt+? to make it more similar: bindkey '^[?' run-help

你甚至可以将它绑定到Alt +?使其更相似:bindkey'^ [?'运行帮助