Expansion of exclamation mark in single quotes in subcommand
version: 4.3.11(5) This seems like a bug, but it seems to have been here for a few years (from the git repository, bash-3.0 displays this behaviour while bash-2.05b doesn't). With history expansion enabled (set +H): $ echo '!!' # good !! $ echo "$(echo '!!')" # not good; !! expands echo "$(echo 'echo '!!'')" echo !! $ echo '$$' # good $$ $ echo "$(echo '$$')" # good $$
Re: Expansion of exclamation mark in single quotes in subcommand
After looking into the code, it turns out it's because history_expand basically works as a finite state machine to determine whether it should expand occurances of ! it runs into, so of course can't handle subcommands (more specifically, quotes inside quotes). This leads to interesting patterns though: echo '!!' # doesn't expand echo "$(echo '!!')" # expands echo "$(echo "$(echo '!!')")" # doesn't expand echo "$(echo "$(echo "$(echo '!!')")")" # expands echo "$(echo "$(echo "$(echo "$(echo '!!')")")")" # doesn't expand The reason it appeared to work in 2.05b was because it simply didn't account for double quotes, which meant it had things like: echo \' !! \' # expands echo "'" !! "'" # doesn't expand This seems like an architectural issue, so I probably wouldn't be able to fix it myself (I've never looked at bash's source until now)—logically, I'd expect the history expansion to happen in the same place as the variable use expansion, but there must be a reason that's not the case.
Re: Expansion of exclamation mark in single quotes in subcommand
> With history expansion enabled (set +H): That should have been -H (+H disables it).
Re: Expansion of exclamation mark in single quotes in subcommand
It's not just inside a double-quoted block. It's inside a single-quoted block. The last two commands in my first email demonstrate that the $ expansion is aware of that, so it follows the rule of everything inside single-quote blocks being literal except for the single quote itself. ! doesn't. echo "$(echo "$$")" # here, $$ is double-quoted, so should be expanded to something (it is) echo "$(echo '$$')" # here, $$ is single-quoted, so shouldn't be expand to something (it isn't) echo "$(echo "!!")" # here, !! is double-quoted, so should be expanded to something (it is) echo "$(echo '!!')" # here, !! is single-quoted, so shouldn't be expanded to something (it is—this seems wrong) I think my second email shows even more clearly that it's a bug: each time you nest a subcommand, the behaviour changes: it alternates between expanding an non-expanding On 22 April 2014 02:26, Chris Down wrote: > Maxdamantus writes: >> This seems like a bug, but it seems to have been here for a few years >> (from the git repository, bash-3.0 displays this behaviour while >> bash-2.05b doesn't). >> >> With history expansion enabled (set +H): >> >> $ echo '!!' # good >> !! >> $ echo "$(echo '!!')" # not good; !! expands >> echo "$(echo 'echo '!!'')" >> echo !! >> $ echo '$$' # good >> $$ >> $ echo "$(echo '$$')" # good >> $$ > > I'm not totally sure why you think this is a bug. How can bash know what > quoting style was used inside the command substitution? This is normal > behaviour when history expansion is performed inside a double quoted > block. > > Perhaps if you explained the reason you think this is a bug better, > someone could give a more meaningful reply. :-)
Re: Expansion of exclamation mark in single quotes in subcommand
On 22 April 2014 04:24, Maxdamantus wrote: > Yeah, I can see what you're saying, but it would probably lead to the > bash-2 behaviour, which didn't observe double quotes. > > Given: echo "$(echo "foo")" > I can't see how it would be desirable for anything to consider > '"$(echo "' and '")"' to be quoted and the rest, 'echo ' and 'foo' to > be unquoted. > With the bash-2 behaviour, the only quotes history expansion is aware > of is single quotes, which can't contain another set of single quotes. > > It looks like the current behaviour was added into bash-3 to make > history expansion more consistent with other expansions, but without > realising double quotes can be nested in the somewhat separate bash > language. > Maybe I should show some practical-looking commands that > (unexpectedly) runs into the problem: > > $ sed '3,10!d' > $ m="$(sed '3,10!d' -bash: !d': event not found > > Ironically, this actually works if the user instead writes: m="$(!!)" > In the process, it will print out the invalid command just above as if > that's the one it's executing. > > On 22 April 2014 03:55, Chris Down wrote: >> Chris Down writes: >>> !! is not single-quoted in the ultimate expansion (bash knows nothing >>> about what happens inside the subshell), which is all that matters. It's >>> not too late to perform history expansion (which is different than >>> parameter expansion). >> >> To be more clear, the history expansion is performed immediately after >> the line is read, before word splitting takes place. >> >> Perhaps this may be undesirable(?), but I don't think it's unexpected >> behaviour.