7

If I type in this:

echo "Hello, World!"

I don't know the name of it, but it prompts me for the next line. You know the PS2 thing. Or if you type echo \ and press Enter.

Why?

Well I know that ! is a Special Variable that you can use to reference your history.

But as soon as I use this:

echo "Hello, World"!

I get my desired output. What is happening and why can't you use ! inside ""?


Thanks for your help :)

Bog
  • 989
  • set +H is your friend to turn off history expansion. Personally, I keep that in my .bashrc everywhere. – Charles Duffy May 13 '23 at 20:08
  • 1
    use single quotes (') instead of double quites (") : they will prevent bash to attempt history expansion on the (!) – Olivier Dulac May 13 '23 at 20:55
  • 2
    As @roaima already mentioned, you're most probably using zsh, not bash. You can check your shell with echo $0 – GypsyCosmonaut May 14 '23 at 00:22
  • What shell are you actually using? If you're not sure, you can use echo $0 to find out: https://askubuntu.com/questions/590899/how-do-i-check-which-shell-i-am-using, https://stackoverflow.com/questions/3327013/how-to-determine-the-current-interactive-shell-that-im-in-command-line – Solomon Ucko May 14 '23 at 02:54

2 Answers2

11

! is not a special _variable. (There's a variable called !, which you can access with $!, but it's unrelated.) It's a character with a special meaning, depending on where bash sees it and on what comes after.

The ! character starts history expansion. Bash performs history expansion very early when parsing a command line, when it's reading commands interactively (not when it's running a script, even a script sourced with . or source from an interactive command line). You can set the variable histchars to select a different character instead of ! (but most other ASCII characters would conflict with common usage).

The character ! starts a history substitution

except when followed by a space, tab, the end of the line, ‘=’ or ‘(’ (when the extglob shell option is enabled using the shopt builtin).

So things like echo "Hello, world"! or if ! grep -q foo myfile; … don't trigger any history expansion.

Also single quotes and dollar-single quotes protect from history expansion (i.e. the history expansion character does not have its special meaning when within '…'), and a backslash that's quoting a character protects that character from starting a history expansion, but double quotes do not, except (since bash 4.4) when bash is invoked in POSIX compatibility mode. That is, by default,

echo "!foo"

makes !foo a history reference which is substituted inside the double quotes. You can't simply include an actual exclamation mark in a double-quoted string, because echo "\!foo" would include the backslash. You have to use something like echo \!"foo" or echo '!foo'. I don't know why bash does this: it's a design decision made in the 1980s. (Bash inherits history expansion from csh where it's even worse: even single quotes don't protect from history expansion.)

I can't reproduce this exact case though:

bash-5.0$ echo "Hello, World!"
Hello, World!

But it might depend on the version or configuration of bash. The double quotes themselves don't inhibit history expansion: it's the fact that !" doesn't fit any of the forms of event designators.

bash-5.0$ echo "Hello!world"
bash: !world: event not found`
  • "When using the shell [...] the history expansion character is also treated as quoted if it immediately precedes the closing double quote in a double-quoted string." -- That's probably why it works for you, the OP seems to use another shell than bash. – Philipp Imhof May 13 '23 at 19:50
  • Whoopsie my bad, yeah you are right I am using zsh, completely forgot. Really detailed explanation from you thanks, I learned some new stuff :) But still, I don't get it why history expansion is triggered by "nothing". Your answer even said "The end of the line does not trigger it", yet still it does. Am I understanding something wrong? ^^ – Bog May 15 '23 at 08:56
  • @Pixelbog In echo "Hello, world!" the ! character is not at the end of the line. – Gilles 'SO- stop being evil' May 15 '23 at 09:05
  • @Gilles'SO-stopbeingevil' Oh it is not? What else is at the end of the line then? ^^ – Bog May 15 '23 at 09:07
  • @Pixelbog The last character is a double quote. – Gilles 'SO- stop being evil' May 15 '23 at 09:58
  • @Gilles'SO-stopbeingevil' ahhhhhhh oh okay. Now it makes sense, thank you very much :) – Bog May 15 '23 at 10:13
6

Assuming you are using bash as tagged, use single quotes instead of double

echo 'Hello, World!'
Hello, World!

I think it's more likely you're using zsh, though, and the same fix applies:

echo "Hello, World!"
dquote>

echo 'Hello, World!' Hello, World!

See What is the difference between the "...", '...', $'...', and $"..." quotes in the shell? for more information on the different types of quotes and when (not) to use them.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287