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?
source to share
?
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
(alsoNULL_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 bylsx
, 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
source to share