Why does echo "$(fc -l -1)" show the previous command, but echo "$(fc -l -1 | cat)" show the current command?
$ testfc () {
> echo "$(fc -l -1)"
> echo "$(fc -l -1 | cat)"
> }
$ echo foo
foo
$ testfc
1820 echo foo
1821 testfc
More detail
I was testing things for a rabbit-hole question and ended up down another rabbit hole. I created this function to try different ways of accessing the previous or current command history number with the fc and history built-ins:
testfc () {
printf 'fc1\t'; fc -l -1
printf 'fc1|\t'; fc -l -1 | cat
printf '(fc1)\t%s\n' "$(fc -l -1)"
printf '(fc1|)\t%s\n' "$(fc -l -1 | cat)" # this one is weird
printf 'fc0\t'; fc -l -0
printf 'fc0|\t'; fc -l -0 | cat
printf '(fc0)\t%s\n' "$(fc -l -0)"
printf '(fc0|)\t%s\n' "$(fc -l -0 | cat)"
printf 'hist\t'; history 1
printf 'hist|\t'; history 1 | cat
printf '(hist)\t%s\n' "$(history 1)"
printf '(hist|)\t%s\n' "$(history 1 | cat)"
str='\!'
printf '@P\t%s\n' "${str@P}"
printf 'HC\t%s\n' "$HISTCMD"
}
Generally, fc -l -0 & history 1 show the current command, and fc -l -1 shows the previous command. Their outputs don't change when run in a $() subshell or piped through cat. Except "$(fc -l -1 | cat)"!
1831 $ echo foo
foo
1832 $ testfc
fc1 1831 echo foo
fc1| 1831 echo foo
(fc1) 1831 echo foo
(fc1|) 1832 testfc # <-- WHAT?
fc0 1832 testfc
fc0| 1832 testfc
(fc0) 1832 testfc
(fc0|) 1832 testfc
hist 1832 2025-05-02 15:10:59 testfc
hist| 1832 2025-05-02 15:10:59 testfc
(hist) 1832 2025-05-02 15:10:59 testfc
(hist|) 1832 2025-05-02 15:10:59 testfc
@P 1832
HC 1832
1833 $ fc -l -2
1831 echo foo
1832 testfc
Context
$ echo $BASH_VERSION
5.2.37(1)-release
$ type fc
fc is a shell builtin
$ type history
history is a shell builtin
$ type cat
cat is /usr/bin/cat
fcin a subshell writes its own history into$HISTFILE, with a 4K buffer. That's the history that(fc)searches. Outside a subshell,fcaccesses the normal$HISTORY.| catchange its output?