Search code examples
bashfindcygwin

Bash: find -exec from a function


I want to use a find -exec from a bash function. I know I need to terminate the -exec with a {} \;, but I can't find the proper way of doing it!

I tried :

find $SEARCHPATH -name \"${FILEPAT}.[ch]\" -exec grep --color -aHn \"$GREPPATTERN\" {} \;
find $SEARCHPATH -name \"${FILEPAT}.[ch]\" -exec grep --color -aHn \"$GREPPATTERN\" '{}' \;
find $SEARCHPATH -name \"${FILEPAT}.[ch]\" -exec grep --color -aHn \"$GREPPATTERN\" \{\} \;
find $SEARCHPATH -name \"${FILEPAT}.[ch]\" -exec grep --color -aHn \"$GREPPATTERN\" '{}' ';'

And many other, but I can't get anything to work. Most of the time I get find: missing argument to '-exec' And when find accepts the syntax I get no results for simple request like:

find . -name "*.[ch]" -exec grep --color -aHn "e" {} \;

Would someone help me with this one? Thanks!


Solution

  • Don't escape the quotes around the arguments -- when you do that, the quotes are treated as part of the arguments, and it's e.g. looking for files with actual quotes in the filename. Use something like this:

    f() {
        searchpath=$1
        filepat=$2
        greppattern=$3
        find "$searchpath" -name "$filepat.[ch]" -exec grep --color -aHn "$greppattern" {} \;
    }
    

    [EDIT] To expand my comment about the quotes being treated as part of the arguments: quotes in a command line affect how the text inside them is parsed. Double-quotes allow variable references ('$varname`) to be expanded and a few other things, but not much else. Once they've had this effect, they are removed (i.e. they are not passed on to the command itself). To see this, let's define a function that just prints its arguments, and see what it actually receives from various command lines:

    $ printargs() { printf "  <%s>\n" "$@"; }
    $ printargs one two
      <one>
      <two>
    $ printargs "one" "two"  # The quotes will be removed before printargs sees them
      <one>
      <two>
    $ printargs "one two"   # The quotes make the two words into one argument, but again aren't passed on to printargs
      <one two>
    $ printargs "one" 'two' three   # Single-quotes are also removed
      <one>
      <two>
      <three>
    

    Similar things happen with variables:

    $ var="one two"   # Note that the quotes are removed before the value is stored in $var
    $ printargs $var    # Variable is substituted, then its value is parsed into separate words
      <one>
      <two>
    $ printargs "$var"   # Double-quotes here allow the variable to be expanded, but prevent further parsing
      <one two>
    $ printargs '$var'   # Single-quotes prevent even variable expansion
      <$var>
    

    If you escape the quotes, however, they don't have any of these effects; they're just treated as parts of the argument:

    $ printargs \"one\" \'two\' \"three four\"
      <"one">
      <'two'>
      <"three>
      <four">
    $ printargs \"$var\"
      <"one>
      <two">
    

    ...this is almost never what you want. In particular, with the find command:

    $ searchpath=.
    $ filepat='*'
    $ greppattern='#ifdef'
    $ printargs "$searchpath" -name "$filepat.[ch]" -exec grep --color -aHn "$greppattern" {} \;
      <.>
      <-name>
      <*.[ch]>
      <-exec>
      <grep>
      <--color>
      <-aHn>
      <#ifdef>
      <{}>
      <;>
    $ printargs "$searchpath" -name \"$filepat.[ch]\" -exec grep --color -aHn \"$greppattern\" {} \;
      <.>
      <-name>
      <"*.[ch]">
      <-exec>
      <grep>
      <--color>
      <-aHn>
      <"#ifdef">
      <{}>
      <;>
    

    ...in the second one (with escaped quotes) the quotes are passed to find and it treats them as part of the filename pattern, looking for files with double-quotes in the name. If it manages to find any, the same thing would happen with the grep command -- it'd be looking for #ifdef's with double-quotes around them.