#18: GNU find operators

Posted by | Comments (0) | Trackbacks (2)

This is not a tutorial about find in general as you might expect it. find, and the GNU variant in particular, is very complex and can't be covered in one short blog post and actually, there are many good tutorials on the Internet. Instead, this blog post concentrates on one single aspect of find: operators.

find lets you specify lots of parameters to find only those files you want. But these parameters can't be set in arbitrary order.

find -print -name foobar

is something different than

find -name foobar -print

The first gives you something like this:

.
./foobar
./foo
./bar
./bla
./blablub
./blub

and the second something like this:

./foobar

Why? The answer is simple. find reads the parameters from left to right and connects them with a binary AND. Each parameter has a return value. -print returns always TRUE, -name returns either TRUE or FALSE, depending on whether matching files are found or not. So in the first example, -print is evauated first, then -name foobar. Together they return TRUE because -print always returns TRUE and -name foobar has actually found a file with that name. In the second example, -name foobar is evaluated first, which finds one file. Then -print is evaluated, which operates on the found file. Again, both parameters together return TRUE. But what if one of the parameters returns FALSE? Let's see:

find -print -name doesnotexist

Whoops? The output is again:

.
./foobar
./foo
./bar
./bla
./blablub
./blub

both parameters together return FALSE, because one of them has failed. But it is actually the second one. At that time when the second one is evaluated, the first parameter has already printed its stuff to the screen. Because the parameters are connected with an AND operator, no further parameters would be evaluated in this row. You can prove that by reversing the order:

find -name doesnotexist -print

This outputs nothing because the first parameter returns FALSE, causing the second parameter to be ignored. For the sake of testing such things, find also provides the parameters -true and -false, which obviously always return TRUE and FALSE. I'll use these for further explanation.

By default, all parameters are connected with AND, but you can also connect them with OR. find provides an operator for this. The operator for OR is -o. OR works the other way round: if the first parameter is FALSE, the second one is evaluated, if that one is FALSE, the third one and so on. If one parameter produces TRUE, all following parameters are ignored. That's because the condition is then fulfilled: one OR the other operand is TRUE. If no operand returns TRUE, the complete OR chain is FALSE.

# -print is evaluated
find -false -o -print

# -print is NOT evaluated
find -true -o -print

Besides OR, find also provides an explicit AND operator, which is -a. You can omit it as it's the default operator if none is specified.

You can also negate an expression. For this you can use the ! operator:

find \! -false -print

This negates -false (which then becomes TRUE), thus -print is executed. Mind the backslash before the exclamation point. Because this is a shell command character, it has to be escaped. Also the whitespace behind it is important, otherwise find wouldn't understand the expression.

As in most programming languages, find also provides a parentheses for grouping expressions:

find \( -true -o -false \) -a -print

Again, don't forget the backslashes and whitespace. Without the parentheses, find would output nothing since -true is already TRUE. All the other parameters would not be evaluated anymore.

All these parameters have an impact on the following expressions. But what if you want to have all expressions evaluated, regardless of whether an earlier one has succeeded or failed? For this, find provides the comma operator:

find -name foo , -name bar

When you execute this, you would only see

./bar

That's because only the output of the last command is returned, but the first is evaluated anyway. You can prove this:

find -name foo -print , -name bar -print

This would output

./foo
./bar

You see, with the comma operator you can operate on several files matching different patterns at once. The advantage here is that find has to traverse the directory tree just once. If you have many files, this could save a lot of time, whereas calling find twice would take a lot longer.

Note: GNU find provides alias names for the operators. So instead of ! you could also write -not, instead of -a you could write -and and instead of -o you could also write -or. However, these names are not POSIX compliant and therefore not very portable. You should instead use !, -a and -o.

Read more about find:

Trackbacks

Manko10 sent a Trackback on : (permalink)

RT @reflinux: #Advent series “24 Short #Linux #Hints”, day 18: #GNU #find operators http://bit.ly/fzg91i

robo47 sent a Trackback on : (permalink)

RT @reflinux: #Advent series “24 Short #Linux #Hints”, day 18: #GNU #find operators http://bit.ly/fzg91i

Comments

No comments have been submitted yet. Be the first!

Write a comment:

HTML-Tags will be converted to Entities.
Textile-formatting allowed
Standard emoticons like :-) and ;-) are converted to images.
Design and Code Copyright © 2010-2017 Janek Bevendorff Content on this site is published under the terms of the GNU Free Documentation License (GFDL). You may redistribute content only in compliance with these terms. tweetbackcheck