>>14
http://partmaps.org/era/unix/award.html
The Useless Use of Cat Award
The venerable Randal L. Schwartz hands out Useless Use of Cat Awards from time to time; you can see some recent examples in Deja News. (The subject line really says "This Week's Useless Use of Cat Award" although the postings are a lot less frequent than that nowadays). The actual award text is basically the same each time, and the ensuing discussion is usually just as uninteresting, but there are some refreshing threads there among all the flogging of this dead horse.
The oldest article Deja News finds is from 1995, but it's actually a followup to an earlier article. By Internet standards, this is thus an Ancient Tradition.
Exercise: Try to find statistically significant differences between the followups from 1995 and the ones being posted today.
(See below for a reconstruction of the Award text.)
Briefly, here's the collected wisdom on using cat:
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.
The fact that the same thread ("but but but, I think it's cleaner / nicer / not that much of a waste / my privelege to waste processes!") springs up virtually every time the Award is posted is also Ancient Usenet Tradition.
Of course, as Heiner points out, using cat on a single file to view it from the command line is a valid use of cat (but you might be better off if you get accustomed to using less for this instead).
In a recent thread on comp.unix.shell, the following example was posted by Andreas Schwab as another Useful Use of Cat on a lone file:
{ foo; bar; cat mumble; baz } | whatever
Here, the contents of the file mumble are output to stdout after the output from the programs foo and bar, and before the output of baz. All the generated output is piped to the program whatever. (Read up on shell programming constructs if this was news to you :-)
Other Fun Awards
This could evolve into a good listing of "don't do that" shell programming idioms.
Useless Use of Kill -9
Randal also posts his Useless Use of Kill -9 Award although much less frequently.
(See below for a reconstruction of the Award text. It explains the issues clearly enough.)
Useless Use of echo
This is really a special case of Useless Use of Backticks but it deserves its own section because it's something you see fairly frequently.
The canonical form of this is something like
variable="something here, or perhaps even the result of backticks"
some command -options `echo $variable`
Depending a little bit on what exactly you have the variable for, this can be reduced at least to
variable="something here, or perhaps even the result of backticks"
some command -options $variable
and there is often no real reason to even think of using echo in backticks when the simpler construct will do.
(There is a twist: echo will "flatten" any whitespace in $variable into a single space -- unless you double-quote $variable, of course --, and sometimes you can legitimately use echo in backticks for this side effect. But that's rarely necessary or useful, and so most often, this is just a misguided use of echo.)
There is another example in the next section, and a longer rant about Useless Use of Backticks further down the page. There is also a parallel, slightly different example on the Backticks Example page
Useless Use of ls *
Very clever. Usually this is seen as part of a for loop:
for f in `ls *`; do
command "$f" # newbies will often forget the quotes, too
done
Of course, the ls is not very useful. It will just waste an extra process doing absolutely nothing. The * glob will be expanded by the shell before ls even gets to see the file names (never mind that ls lists all files by default anyway, so naming the files you want listed is redundant here).
Here's a related but slightly more benign error (because echo is often built into the shell):
for f in `echo *`; do
command "$f"
done
But of course the backticks are still useless, the glob itself already does the expansion of the file names. (See Useless Use of echo above.) What was meant here was obviously
for f in *; do
command "$f"
done
Additionally, oftentimes the command in the loop doesn't even need to be run in a for loop, so you might be able to simplify further and say
command *
A different issue is how to cope with a glob which expands into file names with spaces in them, but the for loop or the backticks won't help with that (and will even make things harder). The plain glob generates these file names just fine; click here for an example. See also Useless Use of Backticks
Finally, as Aaron Crane points out, the result of ls * will usually be the wrong thing if you do it in a directory with subdirectories; ls will list the contents of those directories, not just their names.
Useless Use of wc -l
This is my personal favorite. There is actually a whole class of "Useless Use of (something) | grep (something) | (something)" problems but this one usually manifests itself in scripts riddled by useless backticks and pretzel logic.
Anything that looks like
something | grep '..*' | wc -l
can usually be rewritten like something along the lines of
something | grep -c . # Notice that . is better than '..*'
or even (if all we want to do is check whether something produced any non-empty output lines)
something | grep . >/dev/null && ...
(or grep -q if your grep has that).
If something is reasonably coded, it might even already be setting its exit code to tell you whether it succeeded in doing what you asked it to do; in that case, all you have to check is the exit code:
something && ...
I used to have a really wretched example of clueless code (which I had written up completely on my own, to protect the innocent) which I've moved to a separate page and annotated a little bit. It expands on the above and also has a bit about useless use of backticks (q.v.)
Here's a contribution I got from Aaron Crane (thanks!):
grep -c can actually solve a large class of problems that grep | wc -l can't. If what interests you is the count for each of a group of files, then the only way to do it with grep | wc -l is to put a loop round it. So where I had this:
grep -c "^~h" [A-Z]*/hmm[39]/newMacros
the naive solution using wc -l would have been
for f in [A-Z]*/hmm[39]/newMacros; do
# or worse, for f in `ls [A-Z]*/hmm[39]/newMacros` ...
echo -n "$f:"
# so that we know which file's results we're looking at
grep "^~h" "$f" | wc -l
# gag me with a spoon
done
and notice that we also had to fiddle to get the output in a convenient form.
Useless Use of grep | awk and grep | sed
Here's another one:
ps -l | grep -v '[g]rep' | awk '{print $2}'
(Of course, this is merely an example. If you have lsof it's probably a better solution to this particular problem; also the output of ps varies wildly from system to system so you might want to print something else than $2 and use completely different options to ps.)
Remember that sed and awk are glorified variants of grep. So why use grep at all?
ps -l | awk '!/[a]wk/{print $2}'
Usually you'd like the regex to be tighter than this, especially if your login might happen to include the letters grep or awk ...
True Story from Real Life: an older version of the GNATS system would think my real name was "System Operator" because it just went looking for the first occurrence of the letters e-r-a in the /etc/passwd file. (Well, actually, it thought my name was "System Era". It took me a while to figure out how it arrived at this somewhat whimsical conclusion. Incidentally, you also have to wonder why the author thought my real name was worth knowing, and if this is the right way to get that information. The end goal was to produce a template for an e-mail message -- perhaps my MUA would already know my real name, and even be able to produce nice e-mail headers for GNATS?)