Skip to main content

Logical Operators

find expressions are boolean logic equations. By default, find connects multiple predicates with an implicit AND (-a). However, you can construct complex queries using explicit OR (-o), NOT (!), and grouping operators.

1. The AND Operator (-a / implicit)

If you list multiple tests side-by-side, a file must pass all of them to be acted upon.

# Explicit AND
find . -type f -a -name "*.sh"

# Implicit AND (Identical behavior)
find . -type f -name "*.sh"

2. The OR Operator (-o)

If you want a file to match either of two conditions, use -o.

# Find files ending in .jpg OR .png
find /var/www/images -type f -name "*.jpg" -o -name "*.png"

The Action Binding Trap

When you use -o in conjunction with an action like -print or -delete, you must be extremely careful. Actions bind tightly to the test immediately preceding them.

WRONG:

# This will ONLY delete .png files. It will implicitly print .jpg files.
find /var/www/images -type f -name "*.jpg" -o -name "*.png" -delete

To fix this, you must group the OR conditions.

3. Grouping with Parentheses (\( ... \))

To control precedence and ensure actions apply to the entire logical statement, enclose the OR conditions in parentheses. Because parentheses have special meaning to the shell, you must escape them with backslashes.

CORRECT:

# Deletes both .jpg and .png files safely
find /var/www/images -type f \( -name "*.jpg" -o -name "*.png" \) -delete

4. The NOT Operator (! or -not)

To invert a condition, place ! or -not before it.

# Find all files that are NOT owned by root
find /etc -type f ! -user root

You can combine NOT with grouping to exclude entire classes of files.

# Find all files EXCEPT those ending in .log or .tmp
find /var/app -type f ! \( -name "*.log" -o -name "*.tmp" \)

De Morgan's Laws in Find

When constructing complex find queries, the standard rules of boolean algebra apply.

NOT (A OR B) is equivalent to NOT A AND NOT B.

# These two commands are functionally identical:

# Approach 1: Grouping with OR
find . -type f ! \( -name "*.txt" -o -name "*.md" \)

# Approach 2: Consecutive NOTs with implicit AND
find . -type f ! -name "*.txt" ! -name "*.md"