Can I make leading exclamation points be ignored in the terminal? (I type them by instinct due to ipython)
In interactive shells, by default, !
is used for history expansion: the shell will look for a command matching the text following !
, and execute that. As indicated in this answer to Can't use exclamation mark (!) in bash? shells allow history expansion to be disabled:
- Bash:
set +H
- Zsh:
set -K
If you never want to use history expansion driven by exclamation marks, you can add these to your shell’s startup scripts.
These won’t cause !
to be ignored, but they will avoid running commands from your history. !command
will complain that !command
doesn’t exist.
Shell scripts aren’t affected by these settings, they get their own shells with a non-interactive configuration (which doesn’t include history expansion by default).
If you never use ! from the terminal, you can assign it to a function you also never use, like (in my case) werase:
stty werase !
Then whenever you type ! , it will just erase the previous word. If you only ever type it at the start of a terminal command, then this will simply have no effect.
If you ever need an actual exclamation point, you can still enter it by preceding it with CTRL-V (or whatever your lnext
character is) to quote it / escape it.
If you don't normally use the lnext
functionality, you might find it more convenient to redefine lnext
to be !
instead; that would allow you to enter an exclamation point by entering it twice (the first one quoting the second).
stty lnext !
A more detailed explanation of how this works:
The lnext
functionality is: "do not interpret the next character as anything special: just accept the character as an input just as it is". If it is an stty special character, then do not apply that special functionality, but just use the character as itself. This includes the character assigned to the lnext
function.
The default lnext
character is CTRL-V (mnemonic: "verbatim", as in: enter the next character verbatim). Here, we reassign that function to the character !
instead.
lnext
is chosen primarily because its effect is "no effect" for most cases (e.g., when the next character typed is a normal character). When you accidentally type !
at the start of the line, the next character is almost always a normal character, with no special action, so quoting/escaping it (treating it explicitly as a normal character) does no harm. When we write an a
after an !
, the meaning changes from "write an a
" to the a more specific meaning that does the same.: "write an a
, an ignore anything else, just do it!"
If we want to create an actual !
character, we can use that lnext
functionality to quote it. So, by typing !!
, a single !
is entered.
If using the zsh
shell, you could add to your ~/.zshrc
:
zmodload zsh/zselect
bang() {
zle self-insert
if [[ $CONTEXT = start && $LBUFFER =~ '^!+$' ]]; then
local -a old_region_highlight=($region_highlight)
region_highlight+=("0 $CURSOR fg=red,bg=white,bold,standout")
zle -R 'Will you please stop?!'
zselect -t 100 -r 0 < /dev/tty
region_highlight=($old_region_highlight)
LBUFFER=
fi
}
zle -N bang
bindkey '!' bang
So that when you enter one or more !
at the beginning of the editing buffer, it highlights them in red, gives you a message and removes them (after one second, or sooner if you type anything else).