Do quote me

After Friday’s post was published, Leon Cowle tweeted a good question:

Isn’t it a safer habit to always quote/escape args containing wildcards?

To put this question in context, recall that the command I ran to get all the reports I’d written in the past 60 days was

find . -name *report*.pdf -mtime -60

The command was issued from within my ~/projects directory. Within it are subdirectories for every project and subsubdirectories within each of them for the various aspects of those projects. The idea behind the find command is to search down through the current directory (.) for files with names that match the glob *report*.pdf that were last modified less than 60 days ago.

Leon’s question, which was really a suggestion politely formed as a question, was about my leaving the argument to the -name expression unquoted. He thinks I should have used

find . -name '*report*.pdf' -mtime -60

He’s right. Quoting the argument to -name is a good habit to get into. But it’s a habit I find hard to form.

The reason to quote the argument is to prevent the shell from expanding the glob before find has a chance to get at it. If there were a file at the top level of the ~/projects directory with a name that matched the glob—and if it were less than 60 days old—that would be the only file that find would have returned.

I got away with leaving out the quotes because there were no such files in ~/projects. Except for a couple of SQLite database files, ~/projects has nothing but subdirectories at its top level. I knew that, which is why the command worked without quoting. And although I know that quoting is a safer habit, I wrote the post using the command just as I used it—I didn’t add the quotes to model better behavior than I usually engage in.

It’s not that I never use quotes when working in the shell. But I do tend to forget them more often than I should. One thing I will say in my defense: I build up my shell commands and pipelines incrementally, making sure every step works the way I expect before adding another. I would never write

find . -name something | xargs rm

without first checking the output of

find . -name something

Also, on those rare occasions when I write a shell script, I am much more diligent about quoting than I am when working interactively at the command line. Commands given interactively are written for a particular set of circumstances in which shortcuts can be perfectly fine. Shell scripts get used more widely, where unforeseen conditions are more likely.

There is, by the way, a way to get at the same information without find. If your shell is zsh (or if, like me, you use a recent version of bash installed via Homebrew), you can use the **/ wildcard pattern:

ls -lt **/*report*.pdf | head

In this command, the shell looks down the whole subdirectory tree for files that match the globbing pattern, ls -lt prints them in long form in reverse chronological order, and then head extracts just the first ten, which will be the ten most recent. Because the long form of ls includes the modification date, I could’ve looked through the output of this command and easily determined which reports were written in the past two months.

And because this is a situation where I want the shell to interpret the globbing pattern, quoting the pattern would be wrong. Which is a better fit to my sloppy habits.