I understand the following error is due to ! used for history expansion:

$ echo "Hello!Tim"
bash: !Tim: event not found

However if I put the command into a script and run the script, there is no problem:

$ cat myscript
echo "Hello!Tim"
$ bash myscript 

Why is that? Does the bash manual mention the reason?

  • 1
    Good question, simply stated. Very well done! – Wildcard Aug 9 '17 at 2:41
  • Based on what I'm reading here: gnu.org/software/bash/manual/html_node/History-Interaction.html I believe you would need to use some sort of shopt modifier to get it to work in your script. I know that's not really the answer you are looking for. – jesse_b Aug 9 '17 at 2:51
  • What's the use case? What kind of script would you run that would expand your previous history? Or would it expand previous commands from the script? – Jeff Schaller Aug 9 '17 at 10:58
  • @JeffSchaller, numeric history expansion in a script with positive numbers sounds like a really bad idea, but I could easily imagine use cases for !! or !-2 (negative/relative history numbers) or similar. Mostly they would be solved better in other ways, but it's still a good question about how the shell works (and when you do/don't need to escape your exclams!). – Wildcard Aug 23 '17 at 6:27

Yes, history expansion is enabled by default only for interactive shells.

To enable it for a shell script, write:

set -H

To disable it in an interactive shell, write:

set +H

To determine whether or not history expansion is currently enabled, use some form of the following code:

case $- in (*H*) echo enabled ;; (*) echo disabled ;; esac

In starting to teach a shell class, I dug through the manual extensively to try to establish what an "interactive shell" really is. It's a whirlpool question, so let me save you some trouble:

The shell has MANY options. Some of these options are initialized in different ways when the shell has a controlling terminal (or when started with -i, blah blah, whatever, see below).

ALL of the shell's options can be individually changed.

An "interactive shell" is a deceptive term when you try to define it precisely. It is really just a collection of option settings.

The question about which settings make a shell interactive or not is impossible to answer; it gets ridiculous. It is precisely the same philosophical question as the Ship of Theseus.

If you start an interactive shell, but then disable history expansion, use the --noediting flag, set --norc, turn off expand_aliases, etc., etc., then in what sense is the shell interactive? Or, when does it become not interactive anymore? You can't answer these questions.

The truth is that "interactive" is just a convenient label for a collection of various shell options. Likewise "non-interactive." Same thing; just a collection of behaviors that can each one be changed individually.

Bottom line: the shell behaves differently when it is started "interactively" versus when it is started "non-interactively." Trying to precisely define these terms after start-up is silly. Just look at each individual option of the shell to understand its behavior.

I had forgotten that in addition to my own research, I posted about it extensively on this very site.

  • Thanks. In the manual, I only find "This option is on by default for interactive shells" for set -H. It is probably your inference that "history expansion is enabled by default only for interactive shells". – Tim Aug 9 '17 at 2:56
  • @Tim, see the rest of my answer. That sentence of the manual is a very slippery one, and trying to get the answer to "what IS an interactive shell?" out of the man page is like talking in circles. It goes nowhere. – Wildcard Aug 9 '17 at 3:00
  • What you CAN say is that the following code snippet will always show you whether history expansion is enabled in the current shell: case $- in (*H*) echo enabled ;; (*) echo disabled ;; esac – Wildcard Aug 9 '17 at 3:01
  • @Tim, by the way, I have no idea what you mean that it's my "inference," because that's the clear meaning of set -H. That IS the histexpand option. – Wildcard Aug 9 '17 at 3:03
  • 1
    I've tried this on a script and it didn't work until I also did set -o history, after verifying with set -o from the script that it was disabled. – Daniel C. Sobral Jun 13 '19 at 17:52

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.