How to prevent command execution in ZSH?

I wrote a command line hook:

# 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 this:

> ls?

      

After exiting, man

I get:

> zsh: no matches found: ls?

      

How can I get rid of the wrong command message?

+3


source to share


1 answer


?

is zsh-specific and is a single-character wildcard. This means that if you type ls?

, zsh will try to find matching filenames in the current directory (any three-digit name starting with "ls").

There are two ways to get around this:

  • You can do "?" "leisurely", pointing him ls\?

    , 'ls?'

    or "ls?"

    .

  • You make zsh handle cases where they don't match:

    The default behavior if no match is found is to print an error. This can be changed by disabling the option NOMATCH

    (also NULL_GLOB

    shouldn't be set):

    setopt NO_NOMATCH
    setopt NO_NULL_GLOB
    
          

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

    Caveat: In the (possibly unlikely) case that there is a file with a matching name, zsh will try to run the command with the name of the first matching file. That is, if there is a file named "lsx", it ls?

    will be replaced by lsx

    , and zsh will try to run it. This may or may not fail, but most likely will not have the desired effect.

Both methods have their pros and cons. 1.probably not exactly what you are looking for and 2. doesnt work every time and also changes the behavior of your shells.


Also (as @chepner pointed out in his comment) preexec

works additionally instead of the not command. This means you can get help for ls

, but zsh will still try to start, ls?

or even lsx

(or some other matching name).

To avoid this, I would suggest defining a function command_not_found_handler

instead preexec

. From the zsh manual :

If no external command is found, but a function exists command_not_found_handler

, the shell executes that function with all command line arguments. The function should return zero status if it successfully completed the command, or nonzero status if it failed. In the latter case, standard processing applies: "command not found, printed with standard error, and the shell exits with status 127. Note that the handler is executed in a subshell designed to execute an external command, hence directory changes, shell options, etc. do not affect the main 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 many suitable filenames, but rarely erroneous commands (a common cause of "Command not found" errors), you might want to use this instead:

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

      

It does not mean "?" at the end, but just strips off any last character (note the missing "\" in ${1%?}

) and tries to run man

for the rest. Therefore, even if the file name is the same, it man

will run unless there is actually a command with the same name as the file with the agreement.

Note. ... This will interfere with other tools using command_not_found_handler

for example a tool command-not-found

from Ubuntu (if enabled for zsh).


That said, zsh has a widget called run-help

that can be bound to a key (in Emacs mode it is bound to Alt+ by default H), and does not work man

for the current command.

The main advantages of using the run-help

above are:

  • You can call it at any time when entering a longer command as long as the command name is complete.
  • After you leave the man page, the command is still the same, so you can continue to write on it.

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

+5


source







All Articles