cat
concatenates files given as arguments on command line to standard output, it reads bytes at a time an as default does not perform any interpretation of the bytes it reads.
In your first example you are redirecting stdout to a file, that's why you get a new file.
In your second example the bytes are written to the terminal, and it is the terminal which is interpreting sequences of characters as control sequences for the terminal, this is why you get unusual behaviour on your terminal. It has nothing to do with cat
as such, cat
doesn't know what you are going to do with it's output. You might be sending it through a pipe to another program to interpret/process/print or play "Singing in the rain".
So following the unix philosophy,
do one thing, do one thing only, but do it well
cat
should not attempt to second guess what you are trying to do.
edit 1 reply to @kiwy's 1st comment below.
Yes and No, let me explain,
No, if you cat
to a terminal, because it (the terminal software) is sending the output to your screen or interpreting control sequences (it is emulating an old piece of hardware ie. a teletype device).
but,
Yes if you cat to a pipe and the program recieving can interpret the characters as commands.
look cat this for an example, cat anyOldShellScript | bash
bash will interpret what it gets as commands.
Real (hardware) terminals need that. For instance, with some, the only way to reset them is to do a hardware-reset.
It's harmless with a terminal emulator, and since there's no conventional way to tell the difference (and too hard to determine if some escape sequence might do a hardware-reset), reset
assumes your terminal is real.
The time-delay dates back to tset
in 3BSD in 1979, like this:
/* output startup string */
if (!RepOnly && !NoInit)
{
bufp = buf;
if (tgetstr("is", &bufp) != 0)
prs(buf);
bufp = buf;
if (tgetstr("if", &bufp) != 0)
cat(buf);
sleep(1); /* let terminal settle down */
}
It's evolved somewhat in ncurses, but using the same guideline:
if (!noinit) {
if (send_init_strings(my_fd, &oldmode)) {
(void) putc('\r', stderr);
(void) fflush(stderr);
(void) napms(1000); /* Settle the terminal. */
}
}
Further reading:
Best Answer
Terminals are controlled by escape sequences that are sent in-line with the character data to be displayed. That is how, for example,
echo -e '\e[34m'
will turn the text on many terminals blue. It's echoing some characters to the terminal—they happen to be an escape sequence which sets the foreground color.The terminal was messed up by being instructed to switch into some alternative character set (and possibly a bunch more things). It did that because
/var/log/wtmp
happened to contain the escape sequences used to switch character sets. Technically, it's not really messed up—it's operating exactly as it is designed to. (Trytput smacs
to mess up your terminal on demand;tput rmacs
to change that parameter back.)reset
, etc. function by sending escape sequences resetting various parameters to their defaults. That "fixed" the terminal.That
head /bin/ls
trick is working because your/bin/ls
(or at least the portion printed byhead
) happens to contain an escape sequence changing the terminal parameters back. That's not at all portable—it doesn't work here for example—and likely does a much less thorough job resetting defaults thanreset
, etc.)