tags:

views:

281

answers:

9

I currently use the following command, but it's a little unwieldy to type. What's a shorter alternative?

find . -name '*.txt' -exec grep 'sometext' '{}' \; -print

Here are my requirements:

  • limit to a file extension (I use SVN and don't want to be searching through all those .svn directories)
  • can default to the current directory, but it's nice to be able to specify a different directory
  • must be recursive

UPDATE: Here's my best solution so far:

grep -r 'sometext' * --include='*.txt'

UPDATE #2: After using grep for a bit, I realized that I like the output of my first method better. So, I followed the suggestions of several responders and simply made a shell script and now I call that with two parameters (extension and text to find). Thanks or all the great responses.

+9  A: 

grep has -r (recursive) and --include (to search only in files and directories matching a pattern).

CesarB
It's worth noting that these options are non-portable across unices.
Dominic Eidson
+3  A: 

If its too unweildy, write a script that does it and put it in your personal bin directory. I have a 'fif' script which searches source files for text, basically just doing a single find like you have here:

#!/bin/bash

set -f  # disable pathname expansion

pattern="-iname *.[chsyl] -o -iname *.[ch]pp -o -iname *.hh -o -iname *.cc
-o -iname *.java -o -iname *.inl"
prune=""
moreargs=true
while $moreargs && [ $# -gt 0 ]; do
    case $1 in
    -h)
        pattern="-iname *.h -o -iname *.hpp -o -iname *.hh"
        shift
        ;;
    -prune)
        prune="-name $2 -prune -false -o $prune"
        shift
        shift
        ;;
    *)
        moreargs=false;
        ;;
    esac
done

find . $prune $pattern | sed 's/ /\\ /g' | xargs grep "$@"

it started life as a single-line script and got features added over the years as I needed them.

Chris Dodd
+1  A: 

You could write a script (in bash or whatever -- I have one in Groovy) and place it on the path. E.g.

$ myFind.sh txt targetString

where myFind.sh is:

find . -name "*.$1" -exec grep $2 {} \; -print
Michael Easter
A: 

I usualy avoid the "man find" by using grep $(find . -name "*,txt")

John Nilsson
+2  A: 

I use zsh, which has recursive globbing. If you needed to look at specific filetypes, the following would be equivalent to your example:

grep 'sometext' **/*.txt

If you don't care about the filetype, the -r option will be better:

grep -r 'sometext' *

Although, A minor tweak to your original example will give you exactly what you want:

find . -name '*.txt' \! -wholename '*/.svn/*' -exec grep 'sometext' '{}' \; -print

If this is something you do frequently, make it a function (put this in your shell config):

function grep_no_svn {
    find . -name "${2:-*}" \! -wholename '*/.svn/*' -exec grep "$1" '{}' \; -print
}

Where the first argument to the function is the text you're searching for. So:

$ grep_here_no_svn "sometext"

Or:

$ grep_here_no_svn "sometext" "*.txt"
Jeremy Cantrell
Globbing will run into the limit on the command line size once your filename list gets too long. Combinations of find and xargs won't have this problem.
Liudvikas Bukys
+3  A: 

This is much more efficient since it invokes grep many fewer times, though it's hard to say it's more succinct:

find . -name '*.txt' -print0 | xargs -0 grep 'sometext' /dev/null

Notes:

/find -print0 and xargs -0 makes pathnames with embedded blanks work correctly.

The /dev/null argument makes sure grep always prepends a filename.

Liudvikas Bukys
How about `-H` (`--with-filename`) instead of that workaround?
ephemient
+3  A: 

Install ack and use

ack -aG'\.txt$' 'sometext'
ephemient
This solution is the most succinct, but unfortunately requires an install of ack and is even less portable that a straight find or grep command.
jgormley
ack is a single Perl script. You can download it with wget and put it in your ~/bin directory. That's all there is to it.
Andy Lester
@Andy: it's still a dependency, regardless of how easy it is to get and install
Jeremy Cantrell
+2  A: 

I second ephemient's suggestion of ack. I'm writing this post to highlight a particular issue.

In response to jgormley (in the comments): ack is available as a single file which will work wherever the right Perl version is installed (which is everywhere).

Given that on non-Linux platforms grep regularly does not accept -R, arguably using ack is more portable.

Rich
Plus, ack will run on Windows, so you can do this where you don't have find and grep.
Andy Lester
A: 

You say that you like the output of your method (using find) better. The only difference I can see between them is that grepping multiple files will put the filename on the front.

You can always (in GNU grep, but you must be using that or -r and --include wouldn't work) turn the filename off by using -h (--no-filename). The opposite, for anyone who does want filenames but has to use find for some other reason, is -H (--with-filename).

Mark Baker