4

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
2
  • 2
    Because fc in a subshell writes its own history into $HISTFILE, with a 4K buffer. That's the history that (fc) searches. Outside a subshell, fc accesses the normal $HISTORY. Commented May 3 at 21:50
  • Then why does | cat change its output? Commented May 4 at 18:20

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.