23

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?

  • I spend most my life In SQL*Plus and use ! to invoke a shell command; so I frequently when back in the shell hit ! by mistake! – Mark Stewart Dec 6 '19 at 18:24
  • 13
    You can find a workaround that works on your machine, but keeping this bad habit might bite you when you are using a shell on another computer. Maybe you should consider remapping ! 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... :) – Federico Poloni Dec 7 '19 at 12:51
  • Could you clarify, please, whether you type just an exclamation mark, or an exclamation mark plus a space? It seems to matter for the most upvoted answer at present. If you're used to just typing ! followed by the command (!ls or whatever), would typing ! ls instead work for you (both in shells and IPython shell)? – T.J. Crowder Dec 7 '19 at 16:32
  • 2
    Why don't you run a %rehashx in your ipython profile file or first thing in your ipython session so you can avoid the! prefixing in the first place – iruvar Dec 7 '19 at 17:01
31

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>. – T.J. Crowder Dec 7 '19 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 runs ls then “negates” its exit code. – Stephen Kitt Dec 7 '19 at 16:45
19

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.

  • 2
    Note that this will also take effect inside cooked-mode interfaces, like python or ed. – wchargin Dec 6 '19 at 4:13
  • 9
    Great suggestion, I think lnext would be even better. If you then want a real !, just type !! – icarus Dec 6 '19 at 6:22
  • lnext will enter the next character quoted – Volker Siegel Dec 6 '19 at 22:02
  • @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. – Volker Siegel Dec 7 '19 at 14:59
  • @IMSoP I edited the answer explaining how that lnext mechanism works. – Volker Siegel Dec 7 '19 at 15:28
5

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.