Because I spend most of my life in the IPython shell, I have a bad habit of prepending terminal commands with exclamation points. Usually, this just leads to an error, but sometimes, it causes something bad to happen. Can I effectively disable the !
functionality in my terminal? And would this risk interfering with any scripts?
3 Answers
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 I do
set +H
in Bash and then try (for instance)!ls
, I get an error saying that the command!ls
wasn't found.! ls
(with the space) works, but the question seems to suggest the OP is only using!
, not!<space>
. Dec 7, 2019 at 16:30 -
3@T.J. right, this doesn’t cause
!
to be ignored entirely, it only ensures that it doesn’t run anything from the history.! ls
(with a space) is a valid shell expression, it runsls
then “negates” its exit code. Dec 7, 2019 at 16:45
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.
-
2Note that this will also take effect inside cooked-mode interfaces, like
python
ored
.– wcharginDec 6, 2019 at 4:13 -
10Great suggestion, I think
lnext
would be even better. If you then want a real !, just type !!– icarusDec 6, 2019 at 6:22 -
-
@IMSoP I do not know more than you: I only copied the description line from the man page of
stty
, sorry for implying to have knowledge I do not have. I'll try to come up with a better description. Dec 7, 2019 at 14:59 -
@IMSoP I edited the answer explaining how that lnext mechanism works. Dec 7, 2019 at 15:28
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).
!
to invoke a shell command; so I frequently when back in the shell hit!
by mistake!!
to give you an electric shock when you type it in Bash; this would be an excellent way to get you rid of that habit in the first place... :)!
followed by the command (!ls
or whatever), would typing! ls
instead work for you (both in shells and IPython shell)?%rehashx
in your ipython profile file or first thing in your ipython session so you can avoid the!
prefixing in the first place