Fri, 24 Jul 2009

Efficient Finds

Recently, I've seen a lot of suggestions along the lines of:

find . -name foo -exec ls -l {} \;

This is incredibly inefficient, because for each and every matching file, ls will be executed. Fortunately, we can do better by using xargs. xargs takes a list of lines from stdin and uses them to build up a command line. It takes special care to not exceed the maximum command line length by splitting the input up into multiple commands if it is needed. So, with that knowledge, we can replace our original command with:

find . -name foo | xargs ls -l

There is one slight problem with this command; it isn't space-safe. xargs splits arguments on whitespace, so "file name" will be incorrectly passed to the command as "file" "name". Fortunately, xargs has an option to delimit parameters by the null character, and as our luck would have it, find has a suitable option to produce output in this format. This means our command is now:

find . -name foo -print0 | xargs -0 ls -l

mlocate can do something similar:

locate foo -0 | xargs -0 ls -l

GNU find has one more trick up its sleeve. It has a modified version of -exec that will do the same thing as xargs, so we could have written our original command as:

find . -name foo -exec ls -l {} +

Every process is sacred; every process is great. If a process is wasted, God gets quite irate. Please make sure you try to use one of the latter forms and not the first form, and make a happy deity. :)

[] | # Read Comments (8) |

Comments

I assume that you are using "ls" as an example command since find has an "-ls" option which will not need any additional processes.
Posted by Kapil Hari Paranjape at Sat Jul 25 02:30:05 2009
I'm a newly converted fan of GNU xargs' -n 1 -P 10 (or whatever) for intensive jobs like compiling or encoding.
Posted by Jon at Sat Jul 25 10:09:45 2009
You could just do

  ls -l **/name

with zsh and bash4.

zsh also has zargs, which is used like this:

  zargs -- **/name -- ls -l
Posted by martin f. krafft at Sat Jul 25 11:04:33 2009
You could just do

  ls -l **/name

with zsh and bash4.

zsh also has zargs, which is used like this:

  zargs -- **/name -- ls -l
Posted by martin f. krafft at Sat Jul 25 11:14:06 2009
"GNU find has one more trick up its sleeve.[...] -exec ls -l {} +"

Just for clarification "-exec ... +" is not a GNUism. It is part of the Susv3/Posix standard and e.g. the find implementations on *BSD supported this before GNU findutils.
Posted by Andreas Metzler at Sat Jul 25 11:46:13 2009
"GNU find has one more trick up its sleeve.[...] -exec ls -l {} +"

Just for clarification "-exec ... +" is not a GNUism. It is part of the Susv3/Posix standard and e.g. the find implementations on *BSD supported this before GNU findutils.
Posted by Andreas Metzler at Sat Jul 25 11:47:16 2009
"GNU find has one more trick up its sleeve.[...] -exec ls -l {} +"

Just for clarification "-exec ... +" is not a GNUism. It is part of the Susv3/Posix standard and e.g. the find implementations on *BSD supported this before GNU findutils.
Posted by Andreas Metzler at Sat Jul 25 11:48:11 2009
Why not simply 'find . -name foo -ls' ?
Posted by Javi at Sun Jul 26 23:32:27 2009

Name:


E-mail:


URL:


Comment:


Please enter "fudge" to prove you are a human