Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I want to execute a command for an each file that matches a pattern. But the command that seems to be right, doesn't work for me, I have no idea why.

$ find . -type f -name '*.c' -or -name '*.h' -or -name '*.cpp' -exec ls {} \;
$ ls
script.sh  test.c  test.h
share|improve this question
    
Are you just using -exec ls {} \; as an example action or actually wanting to do something with it? (don't see what purpose it serves in the above command) –  BroSlow Jul 13 at 1:43
    
Yeah, @Bro, I wanted to execute a custom script to a source code files(btw, you may see the one in ls output above). The ls was chosen just as an example. –  Hi-Angel Jul 13 at 18:53

3 Answers 3

up vote 2 down vote accepted

Try adding the expressions into parentheses as stated in the man page:

find . -type f \( -name '*.c' -or -name '*.h' -or -name '*.cpp' \) -exec ls {} \;

should work.

share|improve this answer
    
Yeah, this worked! Thank you very much! Btw, I just searched the man page of the find; I didn't find anything a brackets. What're you talking about? –  Hi-Angel Jul 11 at 13:39
1  
I was talking about the BSD man page of find, which provides the following example: find / \( -newer ttt -or -user wnj \) -print Sorry I was unclear about it. –  groxxda Jul 11 at 13:45
    
That's funny though, that the GNU/Linux manual didn't mention so important thing. –  Hi-Angel Jul 11 at 13:52
    
@YagamyLight The GNU man page uses the more precise word parenthesis to refer to parentheses. So does the FreeBSD man page, for that matter. –  Gilles Jul 11 at 20:03
1  
@YagamyLight I answered a similar question on SO, which may help you understand this better. In particular, this issue here is your original syntax is treated as find . (-type f AND -name '*.c') OR (-name '*.h') OR (-name '*.cpp' AND -exec ls {} \;) –  BroSlow Jul 13 at 1:41

With GNU find, you can use -regex option:

find . -type f -regex '.*\.\(c\|h\|cpp\)' -exec ls {} \;
share|improve this answer
    
I'm not sure about the GNU syntax but on BSD this is the correct way to do it with regex: find -E . -type f -regex '.*(c|h|cpp)$' -exec ls {} \; –  groxxda Jul 11 at 13:43
    
@Groxxda GNU find supports both. It defaults to Emacs regexps, which have a \| operator, unlike FreeBSD, which defaults to BRE. –  Gilles Jul 11 at 20:05
1  
I was merely talking about the .* vs. *.. After testing the command from this answer on GNU find i have verified it's wrong, but my poor reputation won't let me fix it. I don't understand how a not-working answer is up-voted. JIC here is the working GNU find command: find . -type f -regex '.*\(c\|h\|cpp\)' -exec ls {} \; –  groxxda Jul 12 at 11:38
1  
@Groxxda: Sorry, my mistypo. I will make it more accurry. –  Gnouc Jul 12 at 15:06

You can try to use find -D tree . [expr..] to understand what find does with your original command.

You must understand that the -type f and also the -exec ls .. expressions are and'ed to the rest of the expressions with higher precedence than the ors.
So your original command will get parsed into something like this: (-type f AND -name *.c) OR -name *.h OR (-name *.cpp AND -exec ls) (note that the or is actually binary and not ternary so a | b | c is in fact (a | b) | c, but you get the point).
Now you will notice that find does not know what to do except for files matching *.cpp as there is no valid statement in the other cases (that's why you may even see a segfault or something similar in the debug output).

I hope this makes it more clear to you why you need the parentheses.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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