Thursday, July 8, 2010

Cryptic Error Messages--find: paths must precede expression

So, I learned how to use 'find'. find lets you find files in the filesystem. It has a few nice uses. As a software developer who works with make, it is often useful to run:

$ find . -name *.d -delete

which finds all files with the extension .d and deletes them.

Well, yesterday, I was trying to do something a bit complicated, apparently. I wanted to grep for files with a certain pattern but where the filenames end in ",v". So naturally, I typed:

$ find . -name *,v

There was other stuff after that, 'xargs' and 'grep' (don't ask--'cvs' is my mortal enemy and it has to do with that.). But the line above illustrates the issue.

Anyway, I got this error message:

find: paths must precede expression

Huh? Paths must precede expression? What paths? What expression? Is it the comma? Naturally, I had to google to find the answer. What did linux people do before google. I got the answer but still didn't know what was wrong. Then this morning I googled some more and finally learned why it was a problem.

Here it is. The bash shell does something called globbing. That means, when you put wildcards on the command line, it looks for all files that match that pattern and then, if there is something, replaces the expression with the wildcards with that list.

So if I'm in the directory that has no *.d files, *.d doesn't turn into anything and hence, isn't replaced. So the first line above works fine.

But in the case of the *,v file, there were files that matched that. So behold the following example where I make a directory and create 2 empty files in it:

$ mkdir -p /tmp/test
$ cd /tmp/test
$ touch a,v
$ touch b,v
$ find . -name *,v

I'll get an error. Why? Because even though I typed find . -name *,v, the find command doesn't get ".", "-name", and "*,v". No, the *,v is replaced with "a,v b,v" so find gets ".", "-name" "a,v", and "b,v". So it thinks I'm looking for only files named "a,v" and that I want to "b,v" them. I guess... I still don't get the "paths must precede expression" but at least I know why there is a problem.

The solution? Quotes. Double or single. Doesn't matter if you're not using variables.

$ find . -name '*,v'

This does what I want.

Wow! So long since I posted

It's been a long time. But I've been learning linux like crazy. I wish I could say I love it, but I don't. In fact, the GNU tools and shell pretty much drive me bananas.

If I can, I'll try to blog about various things I learned....