echo vs printf

Recently, I caused myself a giant headache because I was uninformed about echo and how it differs from printf (not to mention how it affected my code). Hopefully I can spare you the same pain. For the Software Tools Philosophy to work, one must have a working knowledge of the tools.

Alright, review time. According to man echo, it’s used to “display a line of text”. And so it does! It’s quite handy when you need to dump something to stdout. Especially when that something is literal text.

$ echo cheese and crumpets
cheese and crumpets

I often use echo for this reason, and normally it works in exactly the fashion I expect. However, sometimes what I expect is not reasonable or even logical! I tried to use echo to stitch together a screen stylesheet with a print stylesheet. (I’ve heard that many browser’s download print stylesheets even when they’re not using them. As such, it may as well come in a single http request.) Specifically, I tried to do the following:

echo -n "$(cat screen.css)\n@media print {\n$(cat print.css)\n}\n"

On the surface this looks like a perfectly reasonable approach and, given the correct circumstances, it is. echo dutifully renders the print stylesheet with a wrapping @media declaration. The problem is that the following was included in screen.css:

.clearfix:after, .container:after {
  content:"\0020"; ...

You’ll notice the unicode escape sequence for space (\0200). To echo this looks like an escape sequence (and so it is, just not one we’d like to replace) and so it replaces it. At this point I can hear you saying “But wait! echo has the -E switch for that! It disables interpretation of backslash escape sequences!”. You’re right. It does. It cannot, however, be used granularly. This renders it useless for my task. I need to escape the newlines surrounding the @media declaration, and yet I need to leave the unicode escapes in the stylesheet untouched.

This is where printf saves the day. It works almost identically to the version in stdio.h. It will always replace backslash escapes in the format string and never replace backslash escapes in arguments. In the end, I used the following command:

printf "%s\n@media print {\n%s\n}\n" "$(cat screen.css)" "$(cat print.css)"

This worked like a charm and saved my day. \o/

Hopefully, you’ll remember this next time you start echoing things with escape sequences in them. I have no idea how quotes within the arguments are dealt with but that’s a mystery for another day.