When I'm using

find . -type f -name "*.htm*" -o -name "*.js*" -o -name "*.txt"

it finds all the types of file. But when I add -exec at the end:

find . -type f -name "*.htm*" -o -name "*.js*" -o -name "*.txt" -exec sh -c 'echo "$0"' {} \;

it seems it only prints .txt files. What am I doing wrong?

Note: using MINGW (Git Bash)

  • Hint: the first command will also print directories whose names match *.js* or *.txt.
    – Wildcard
    Sep 8, 2017 at 0:00

2 Answers 2

find . -type f -name "*.htm*" -o -name "*.js*" -o -name "*.txt"

is short for:

find . '(' '(' -type f -a -name "*.htm*" ')' -o \
           '(' -name "*.js*" ')' -o \
           '(' -name "*.txt" ')' \
       ')' -a -print

That is, because no action predicate is specified (only conditions), a -print action is implicitly added for the files that match the conditions.

(and, by the way, that would print non-regular .js files (the -type f only applies to .htm files)).


find . -type f -name "*.htm*" -o -name "*.js*" -o -name "*.txt" \
  -exec sh -c 'echo "$0"' {} \;

is short for:

find . '(' -type f -a -name "*.htm*" ')' -o \
       '(' -name "*.js*" ')' -o \
       '(' -name "*.txt" -a -exec sh -c 'echo "$0"' {} \; ')'

For find (like in many languages), AND (-a; implicit when omitted) has precedence over OR (-o), and adding an explicit action predicate (here -exec) cancels the -print implicit action seen above. Here, you want:

find . -type f '(' -name "*.htm*" -o -name "*.js*" -o -name "*.txt" ')' \
  -exec sh -c 'echo "$0"' {} \;


find . -type f '(' -name "*.htm*" -o -name "*.js*" -o -name "*.txt" ')' -exec sh -c '
   for i do
     echo "$i"
   done' sh {} +

To avoid running one sh per file.

  • In some of those sh -c uses, you need to add the zeroth argument for sh (though in others you included it already). Dec 9, 2013 at 23:14
  • 2
    That's a great answer!
    – Marinos An
    Feb 28, 2018 at 21:05
  • Is -o in the man pages?
    – 9301293
    Nov 22, 2019 at 3:31
  • 1
    It's in the man page: Expressions > Operators > join together the other items within the expression. They include for example -o (meaning logical OR) and -a (meaning logical AND). Where an operator is missing, -a is assumed.
    – klokop
    Aug 19, 2020 at 7:10
  • Thank you so much for this explanation! I've been struggling with find syntax, but that explanation clears a big part of it!
    – Wadih M.
    Jun 15, 2021 at 19:11

It is the implied brackets. Add explicit brackets. \( \)

find . -type f \( -name "*.htm*" -o -name "*.js*" -o -name "*.txt" \) -exec sh -c 'echo "$0"' {} \;

or using xargs ( I like xargs I find it easier, but apparently not as portable).

find . -type f \( -name "*.htm*" -o -name "*.js*" -o -name "*.txt" \) -print0 | xargs -0 -n1 echo

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.