#18: GNU find operators
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 foobaris something different than
find -name foobar -printThe 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 -printThis 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 -printAgain, 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 barWhen 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:
RT @reflinux: #Advent series "24 Short #Linux #Hints", day 18: #GNU #find operators http://bit.ly/fzg91i