Bash: is there a way to search for a particular string in a directory of files?

50

13

Possible Duplicate:
Search for a text pattern in linux

In bash, I was wondering if there were any commands that would let you know if a particular string you are looking for exists within the files located in the current directory you are in.

Say you are looking for the function 'toUpperCase()' within a set of C files in your current directory. There are a lot of files, so you wouldn't want to manually open each one up using vim and check for the string 'toUpperCase' because that would take a lot of time. If not a bash command line, is there another method to do this efficiently?

Dark Templar

Posted 2011-10-10T20:05:44.913

Reputation: 2 329

Question was closed 2011-10-11T19:09:09.417

Answers

68

With grep:

grep -R "toUppercase()" *

Or, if you have ack-grep installed, simply:

ack-grep "toUppercase"

If you want to limit the search in C files, with ack-grep:

ack-grep -cc "toUppercase()"

ack-grep can also be installed on OSX but the executable is called just ack.

Trasplazio Garzuglio

Posted 2011-10-10T20:05:44.913

Reputation: 806

So much so that SO already has a suspiciously similar question ;)

– David Perry – 2011-10-10T20:27:03.203

10

The standard text search tool is grep.

To look for the string toUpperCase( in all .c files in the current directory:

grep -F 'toUpperCase(' *.c

If you want to look in subdirectories as well:

grep -r --include='*.c' -F 'toUpperCase('

If you're looking for C function calls, then ctags may be a better fit. You run it once on all your source files to create an index of identifiers, then use your editor to navigate between calls. All vi-like editors can use ctags's TAGS file. If you use Emacs, run the similar program etags.

Gilles 'SO- stop being evil'

Posted 2011-10-10T20:05:44.913

Reputation: 58 319

ctags is useful for finding function definitions, not function calls. – JdeBP – 2011-10-11T12:17:22.623

6

You're using vim. You've got a tool to do this built in.

Predictably, there are seven answers already saying to use grep. But I seem to be the only person thus far who has noticed from your question that you are using vim. As such, although you can use grep from within vim, you can also use vim's built-in tool. This is invoked via the :vimgrep command.

To search "all of the C source files in the current directory for calls to the function toUpperCase()" one types the vim command

:vimgrep "\<toUpperCase\_s*(" *.c

The resulting list of matches is automatically loaded up into the quickfix list, accessible with either of (see the on-line help for the subtle difference)

:copen
:cwin

To find the function definition, rather than calls to it, ctags is the tool, as mentioned in Gilles's answer, in conjunction with the :tjump or :tselect commands.

Why use :vimgrep?

The on-line help (:help grep) enmumerates several of the reasons, which I won't parrot here. Further to those, compare the action of :vimgrep with that of dietbuddha's answer. dietbuddha's command line forks an individual grep process for each individual C source file. It doesn't even employ xargs to reduce that overhead. And you still have to somehow parse the output to invoke your text editor on the relevant source files once it is finished. :vimgrep doesn't fork off multiple additional processes at all, and using the result is simplicity itself. Merely selecting one of the entries in the resultant quickfix list automatically positions the cursor on the relevant line of the relevant source file.

In fact, it does exactly what you wrote you would do by hand, except automatically. It's the automated way of doing those very text editor actions. It loads up the file as if loaded by hand, searches it for a regular expression (using the same regular expression syntax that you are already using elsewhere in vim), records the places where matches occur, and then unloads the file.

JdeBP

Posted 2011-10-10T20:05:44.913

Reputation: 23 855

2

Yes, the tool is called grep.

The grep command searches one or more input files for lines containing a match to a specified pattern. By default, grep prints the matching lines.

Basic usage is:

grep something somewhere

Or, more specifically:

grep [some options] pattern file

In a very simple case, you recursively want to search through all files, thus you supply the -r option. If you don't care about uppercase or lowercase, make it case-insensitive with the -i option.

grep -ri "touppercase()" .

If you want only the matching parts, use the -o option.

Grep can use regular expressions, and it has a huge manual.

slhck

Posted 2011-10-10T20:05:44.913

Reputation: 182 472

2

grep is the standard answer -- using either the -r flag to search recursively or in conjunction with find (discussion and examples in other questions on this site, such as this one)

My preferred choice lately is ack. It's not likely installed on your computer, but you can install using apt-get on Ubuntu or simply download. Links, instructions, and reasons that it's better than grep are available at http://betterthangrep.com/

Doug Harris

Posted 2011-10-10T20:05:44.913

Reputation: 23 578

1

find and grep

find myproject -name '*.c' -exec grep 'toUpperCase(' {} \; -print

why not only grep?

grep -r string . will recurse through all file in the current directory. If you have object files or other binaries you may get a match back with binary results aka. junk. If you also happen to have fifos or device files in your subdirectory you may have unknown/unintended consequences.

grep string *.c only searches the current directories .c files.

grep string **/*.c does search all subdirectories. However, if you have a very large number of .c files you do run the risk of exceeding the maximum number of expansions for the shell glob. The expansion also has to happen first which means a full tree traversal before it the grep begins.

dietbuddha

Posted 2011-10-10T20:05:44.913

Reputation: 138

0

Grep will do the trick but a bit of additional scripting will help to find the name of the file and the exact line referenced. If that is what you want I find the following works well. I have it saved as a shell script and pass it the value I want as first command line option.

for i in $(ls)
do
    cat $i 2> /dev/null | grep $1 > /dev/null 2>&1
    STATUS=$?
    if [ $STATUS  -eq 0 ] ; then
        echo $i
        cat $i | grep -n $1
    fi
done

Tim Brigham

Posted 2011-10-10T20:05:44.913

Reputation: 1 102

3grep has a -H option which does this already. grep -Hn whatever * – Roy Rico – 2011-10-10T21:33:32.933

Learn something new every day.. Thanks @Roy Rico – Tim Brigham – 2011-10-11T13:05:21.923

0

I find it interesting that no one has answered your specific question: "how do I find whether a particular string exists within the files located in the current directory". As far as I know there is no grep option that only returns whether a match has been found in a group of files. The closest option is -l to list the filename(s). The closest answer among those so far is from tim, with some slight tweaks:

#!/usr/bin/bash

for i in $(ls *.c 2> /dev/null)
do
    cat $i 2> /dev/null | grep $1 > /dev/null 2>&1
    STATUS=$?

    if [ $STATUS  -eq 0 ] ; then
        echo "Found"
        exit
    fi
done

echo "Not found"

You could make this shell script more generic by accepting a second argument for the file extension without too much trouble.

doug

SnoopDougieDoug

Posted 2011-10-10T20:05:44.913

Reputation: 101