Is this a bug in GNU Bash?
I found this out while attempting to shore up my scripts for string security:
$ echo '!!'
!!
$ echo "$(echo '!!')"
echo "$(echo 'e开发者_如何学JAVAcho '!!'')" #<~ the console echoes the line with expanded history here
echo !! #<~ the result
It seems to me that the innermost quoting, which is single-quoted, should not expand anything, variable, subshell, or otherwise, but in this case it expands the !!
to the last line typed. Seems like it shouldn't do that.
I ask you: is this a bug in Bash, and if it is possible to use a quoted subshell expansion that outputs an exclamation mark?
(Using Bash 4.1.007 in Linux)
Edit:
If the above isn't a bug, why, then, does this behave as expected?
$ foo='some value'
$ echo "$(echo 'neither $foo nor `this subshell` should expand here')"
neither $foo nor `this subshell` should expand here
I agree.
$ echo "$(echo '!!')"
echo "$(echo 'echo $(echo '!!')')"
echo $(echo !!)
should do the same as
$ echo $(echo '!!')
!!
I can't see how to explain the difference based on the history expansion documentation.
It's also odd how the history expansion docs are completely separate from the rest of the shell expansions documentation.
zsh echoes !!
for both, at least with my setup.
It may be a bug, but set +H
will disable that (it's off by default in scripts anyway).
Here are the relevant sections from the man page:
Enclosing characters in double quotes preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !. The characters $ and ` retain their special meaning within double quotes. The backslash retains its special meaning only when followed by one of the following characters: $, `, ", \, or <newline>. A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ! appearing in double quotes is escaped using a backslash. The backslash preceding the ! is not removed.
and
History expansion is performed immediately after a complete line is read, before the shell breaks it into words. ... History expansions are introduced by the appearance of the history expansion character, which is ! by default. Only backslash (\) and single quotes can quote the history expansion character.
If you put your two bangs into a variable, you will get the results you expect (and more string security as well).
(
#set -xv
twobangs='!!'
echo "${twobangs}"
echo "$(echo "${twobangs}")"
echo "$(echo 'echo "${twobangs}"')"
echo "$(echo "echo "${twobangs}"")"
echo "$(echo "echo ""${twobangs}")"
#set -xv
)
On a similar note, you sometimes can use the shell's builtin string concatenation and just put a character such as '!' into single quotes to prevent interpretation by the shell.
(
set -H
echo "Hello, world"'!'
(sleep 10) &
#trap "echo exit; kill -TERM $!" EXIT HUP INT QUIT TERM # troublemaker
#trap "echo exit; kill -TERM $"'!' EXIT HUP INT QUIT TERM
trap "echo exit; kill -TERM $"'!'"; echo killed" EXIT HUP INT QUIT TERM
sleep 5
)
精彩评论