Useless use of cat

Prefix Notation | Other Languages | Terminus Obscura

“The purpose of cat is to concatenate (or "catenate") files. If it's only one file, concatenating it with nothing at all is a waste of time, and costs you a process.” — Randal L. Schwartz. The following example reads the contents of file twice, once with cat(1), and again with the second program:

$ cat file | wc -l

Eliminate this useless cat(1) via one of the following methods:

$ wc -l < file
$ wc -l file

wc(1) accepts the file either as an argument or standard input; the best method to use depends on the task. For instance, a rare useful use of cat is to count of lines from multiple files:

$ wc -l /etc/passwd
17 /etc/passwd
$ cat /etc/passwd /etc/group | wc -l
48

Prefix Notation

In an interactive shell, the useless cat file | … syntax may seem attractive when fiddling around with the commands that follow the pipe. However, shells parse the redirect statements prior to executing the command, again eliminating the need for the useless cat via the following notation:

$ <file grep foo
$ <file grep bar

Digression: the redirect-before-command syntax fails before while loops in the sh and bash shells. In these shells, the redirect must be done after the while statement. This is one of several reasons I use zsh:

$ echo foo > file

$ <file while read l; do echo $l; done
while: not found

bash-2.05a$ <file while read l; do echo $l; done
bash: syntax error near unexpected token `do'

zsh-4.0.4$ <file while read l; do echo $l; done
foo

A second digression: other while caveats perhaps worth mentioning: while loops may interact poorly with commands such as ssh. Also, a while loop may not act on the ultimate line, if that line lacks an ultimate newline character.

$(< a_file) can be used to replace the useless $(cat a_file) in some shells, for example to customize the hosts completed on in ZSH:

zstyle -e ':completion:*:*' hosts 'reply=($(< ~/.hosts))'

Other Languages

Other languages, such as Perl, offer file handling interfaces. Abuse of cat in these languages is beyond useless: the pointless shell call can create security issues, make the program subject to any number of shell portability issues, easily runs afoul both shell and Perl quoting and interpolation rules, complicates error handling, and creates software that is nigh impossible to debug—perl -c will check the Perl code, but not any of the shell code. Instead, use the features of the language to read a file. These internal methods, such as open in Perl, are more secure, more portable, and more amenable to special needs, such as binmode calls to properly handle encoded or binary files.

open(my $fh, '<', $filename) or die "error: cannot open $filename: $!\n";
binmode($fh, ':utf8');
while (my $line = <$fh>) {
chomp($line);
next if $line =~ m/…/;


}

Or, consider the convenience of File::Slurp:

use File::Slurp;
my $text = read_file($filename);

Terminus Obscura

Another useless use of cat is to hide the terminal:

$ echo foo >file
$ perl -ple 'print "terminal!" if -t' file
terminal!
foo
$ cat file | perl -ple 'print "terminal!" if -t'
foo

This may be required should the command behave differently on a terminal, and the non-terminal behavior is desired. However, again, there are solutions that do not involve the needless inefficiency of cat. Using the prefix notation, the data can be passed on standard input, which moves standard input of the subsequent program away from the terminal:

$ <file perl -ple 'print "terminal detected" if -t'
foo

The second method is to close standard input. The filename is read by the program itself, not needlessly by cat:

$ perl -ple 'print "terminal detected" if -t' file <&-
foo

The tty(1) command shows how closing standard input hides the terminal:

$ tty
/dev/ttyp1
$ tty <&-
not a tty

Terminal checks should be used sparingly, and consistently, or an option to disable or force the behavior offered in complicated applications, as the invisible change in behavior can cause unexpected results in systems where the terminal or STDOUT are used for other purposes (for example under test frameworks, or in certain FastCGI implementations).