6

I am currently using

find . -name '*.[cCHh][cC]' -exec grep -nHr "$1" {} ; \
find . -name '*.[cCHh]' -exec grep -nHr "$1" {} ;

to search for a string in all files ending with .c, .C, .h, .H, .cc and .CC listed in all subdirectories. But since this includes two commands this feels inefficient.

How can I use a single regex pattern to find .c,.C,.h,.H,.cc and .CC files?

I am using bash on a Linux machine.

doubleDown
  • 8,048
  • 1
  • 32
  • 48
Arpith
  • 570
  • 2
  • 10
  • 26

4 Answers4

16

You can use the boolean OR argument:

find . -name '*.[ch]' -o -name '*.[CH]' -o -name '*.cc' -o -name '*.CC'

The above searches the current directory and all sub-directories for files that end in:

  • .c, .h OR
  • .C, .H OR
  • .cc OR
  • .CC.
Richard Neish
  • 8,414
  • 4
  • 39
  • 69
rid
  • 61,078
  • 31
  • 152
  • 193
  • 3
    Much more elegant than trying to do it all with regexes. – Donal Fellows Oct 19 '12 at 08:27
  • This doesn't work if you want to add "-exec". For example "find -name \*.log' -o -name \*.txt -exec cat '{}' \; " It only executes the first -name – OutputLogic Aug 20 '13 at 16:54
  • which if for some reason you don't realise is `-o` – Crowie Feb 20 '14 at 14:13
  • 5
    @OutputLogic using `find`s precedence operators ( and ) address this e.g. `find \( -name "*.log" -o -name "*.txt" \) -exec cat {} \;` should mean that `-exec` works as expected. – Peter Briggs Jun 10 '14 at 11:56
  • 1
    rid -- would you mind updating your answer so that casual readers don't miss Briggs' point? – John Jan 30 '15 at 19:19
12

This should work

Messy

find . -iregex '.*\.\(c\|cc\|h\)' -exec grep -nHr "$1" {} +

-iregex for case-insensitive regex pattern.

(c|cc|h) (nasty escapes not shown) matches c, cc, or h extensions


Clean

find -regextype "posix-extended" -iregex '.*\.(c|cc|h)' -exec grep -nHr "$1" {} +

This will find .Cc and .cC extensions too. You have been warned.

hornetbzz
  • 9,188
  • 5
  • 36
  • 53
doubleDown
  • 8,048
  • 1
  • 32
  • 48
2

This command works.

find -regextype posix-extended -regex '.+\.(h|H|c{1,2}|C{1,2})$'

I wish I could use iregex. iregex would also find .Cc and .cC. If I could, the command would look like this. Just a bit shorter.

find -regextype posix-extended -iregex '.+\.(h|H|c{1,2})$'
Maksim Dmitriev
  • 5,985
  • 12
  • 73
  • 138
0

find . -regex '.*\.\([chCH]\|cc\|CC\)'
will find all files with names ending in .c,.C,.h,.H,.cc and .CC and does not find any that end in .hc, .cC, or .Cc. In the regex, the first few characters match through the last period in a name, and the parenthesized alternatives match any of the single characters c, h, C, or H, or either of cc or CC.

Note, find's -regex and -iregex switches are analogous to -name and -iname, but the regex-type switches allow regular expressions with | for alternative matches. Like -iname, -iregex is case-insensitive.

The (non-functional) form
find . -name '*.[cCHh][cC]?$'
given in a previous answer doesn't list any names on my linux system with GNU find 4.4.2. Another problem with '*.[cCHh][cC]?$' as a regex is that it will match names like abc.Cc and xyz.hc which are not in the set of .c,.C,.h,.H,.cc and .CC files that you want.

James Waldby - jwpat7
  • 8,593
  • 2
  • 22
  • 37
  • the first form doesn't work because `?` and `$` are not shell patterns so they are interpreted literally – doubleDown Oct 19 '12 at 07:58
  • @rid, I edited to point out that `-iregex '.*\.[ch]\|.*\.cc'` does *not* match .cH. However, I agree that it matches .Cc and .cC and added a `-regex for exact set of extensions – James Waldby - jwpat7 Oct 19 '12 at 08:24