3
votes

I'm trying to understand how/why the cat command parses its stdin the way that it does.

When I set cat's stdin to be "infile.txt", the stdin is simply written to stdout:

~$ echo foo bar test > infile.txt 
~$ cat < infile.txt
foo bar test
~$

This is as I would expect it. However, when I run cat with its stdin set to my (p)tty, stdin is duplicated to stdout until EOF is read:

~$ cat
foo bar test
foo bar test
input is duplicated
input is duplicated
^C
~$

For each pair:

  • the first line is displayed as the user types. (this is NOT written to stdout/stderr)
  • the second line is produced once the user presses [enter]. (this is written to stdout)

How is this possible? As I understand it, sh parses both of the above commands in such a way that cat is called with (argc=1, argv=["cat"]). So, the call to cat is the same. If this is the case, then why isn't the output duplicated in the ~$ cat < infile.txt example?

As cat reads each character from its stdin from infile.txt, shouldn't it ALSO print out the read character (as it does int the simple ~$ cat case)?

I've looked at this simple(ish) implementation [1] but it doesn't behave in the same way as ubuntu 18.04's cat. It doesn't print out the duplicated inputs as in the simple ~$ cat example.

[1] https://gist.github.com/pete/665971#file-unix7-cat-c

2

2 Answers

4
votes

cat only outputs its input once. It's the terminal that also outputs user input.

You can, for example, disable terminal echo by typing

stty -echo

and then (blindly) start cat and start typing - no duplicate output.

1
votes

Basically cat works as follows:

while (block = read_block(input)) {
    write_block(block, output);
}

The block size depends on whether stdin is a tty or a file. When stdin comes from a tty, the input is line buffered otherwise block buffered.