#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 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
:
RT @reflinux: #Advent series "24 Short #Linux #Hints", day 18: #GNU #find operators http://bit.ly/fzg91i