Discussion:
Unexpected "!$" history expansion after use of here-document
Viktor Dukhovni
2018-09-12 00:17:58 UTC
Permalink
Configuration Information [Automatically generated, do not change]:
Machine: amd64
OS: freebsd11.1
Compiler: cc
Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='amd64' -DCONF_OSTYPE='freebsd11.1' -DCONF_MACHTYPE='amd64-portbld-freebsd11.1' -DCONF_VENDOR='portbld' -DLOCALEDIR='/usr/local/share/locale' -DPACKAGE='bash' -DSHELL -DHAVE_CONFIG_H -I. -I. -I./include -I./lib -DDEFAULT_PATH_VALUE='/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin' -DSTANDARD_UTILS_PATH='/sbin:/bin:/usr/sbin:/usr/bin' -DLIBICONV_PLUG -I/usr/local/include -O2 -pipe -DLIBICONV_PLUG -fstack-protector -fno-strict-aliasing -Wno-parentheses -Wno-format-security
uname output: FreeBSD straasha.imrryr.org 11.1-RELEASE-p10 FreeBSD 11.1-RELEASE-p10 #1 r333375M: Sat Jun 23 17:50:31 EDT 2018 ***@straasha.imrryr.org:/usr/obj/usr/src/sys/GENERIC amd64
Machine Type: amd64-portbld-freebsd11.1

Bash Version: 4.4
Patch Level: 23
Release Status: release

Description:
Intuitevely, (and confirmed behaviour of "csh" whose history
substitution bash emulates), after a command of the form:

$ grep foo <<EOF > /tmp/bar
foobar
abcdef
EOF

One would expect "!$" to be /tmp/bar, so that an immediate:

$ wc -l !$

should expand to "wc -l /tmp/bar" and output "1".

Instead we get:

$ PS1='$ '
$ PS2=" "
$ grep foo <<EOF > /tmp/bar
foobar
abcdef
EOF
$ cat !$ > /dev/tty
cat
/dev/tty
abc
abc
123
123
<CTRL-D>
$ history 3
3 grep foo <<EOF > /tmp/bar
foobar
abcdef
EOF

4 cat
/dev/tty
5 history 3

It looks like "!$" expands to the newline after the EOF in history entry 3.
The expansion (as in "csh") should be "/tmp/bar".

Repeat-By:

In interactive shell, expand "!$" after a command that uses a here-document:

$ grep foo <<EOF > /tmp/bar
foo
EOF
$ echo "--!$++"
echo "--
++"
--
++
Chet Ramey
2018-09-15 18:52:05 UTC
Permalink
Post by Viktor Dukhovni
Bash Version: 4.4
Patch Level: 23
Release Status: release
Intuitevely, (and confirmed behaviour of "csh" whose history
$ grep foo <<EOF > /tmp/bar
foobar
abcdef
EOF
$ wc -l !$
should expand to "wc -l /tmp/bar" and output "1".
If you're using command-oriented history, which I surmise you are,
the history entry is the entire previous command, with its embedded
newlines.

The last word is the final newline, since the embedded newlines count
as words (which seems non-intuitive; I will have to look at that).

I don't know what csh does, or why it seemingly throws away the rest
of the command, but that's not how command-oriented history works in
bash.

Chet
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Loading...