2011-07-27

While hacking code and introducing bugs to a project, I sometimes find it necessary to search for a string in every file in a directory. For example, suppose I'm pulling my hair out trying to find where in a drupal project a certain function is called. It could be in a module, or a theme, or the template file. Egads! the code is all over the place and I most certainly do not want to open every damn file and search for the function.

I had been using a script called 'find_in_dir' to help me find a string in a directory and the script is as follows:

find $1 -type f -exec grep -i $2 {} \; -print

The usage of the script was simple:
[jezra@fatlappy ~]$ find_in_dir PATH_TO_SEARCH STRING_TO_FIND

The script would recursively search each file in the directory and output the lines in a file containing the search string and then output the path to the file that was searched. While this definitely did what I needed, I found that I was spending too much time tracking down the path to the file because all of the output was just plain text on the screen.

What I really wanted/needed was something to differentiate the file path in the output from the rest of the output, so I fired up geany and hacked together some Ruby code to do what I need (and output the file path using green text. ohhhhh fancy!)

Enter the Ruby

#!/usr/bin/env ruby

#make a helpy helper function 
def help()
  print "--USAGE--\n"
  print "findindir directory string_to_find\n"
end

#find (
#loc: path to a file or directory,
#string: the string to search for
#)
def find(locstring)
  #is the location a directory?
  if File.directory?(loc)
    #loop through each item in the directory
    Dir.foreach(locdo |name|
      #ignore . and ..
      if name!='.' and name!='..'
        #what is the path of the item?
        path File.join(loc,name)
        #recurse
        find(pathstring)
      end
    end
  else #this is a file 
    #by default, we state that there is no match to the string
    match false
    #loop through each line in the file
    File.foreach(locdo |line|
      #does the line contain our string?
      if line.include?(string)
        match true
        puts line
      end
    end
    #a match was found
    if match
      puts  "\e[32m#{loc}\e[0m\n\n"
    end
  end
end

#set some vars based on user input
start_loc ARGV[0]
string ARGV[1]

#did the user enter enough data?
if start_loc.nil? or string.nil?
  help
else
  #determine the absolute path to the start location
  path File.absolute_path(start_loc)
  #find that shit!
  find(pathstring)
end

Having trouble copying the code? get it at http://hoof.jezra.net/snip/6

The code isn't perfect and could certainly use some improvements, such as:

  • ignore broken symlinks
  • output the line number where matches were found
  • default to current working directory when the user doesn't specify a directory

It may not be the best, but it's a start. Now quit reading and go find something.

Comments
2011-07-27 Kevin Granade:
This get's you most of the way to your solution:
find $1 -type f -exec grep -i $2 {} \; -printf "[%p]\n" | colorit

It has the side effect that it might also colorize other parts of the results, and I didn't quickly see a way to safely remove the brackets after colorization.
2011-07-27 jrobb:
very cool, jez. I'll look through this, ruby seems pretty interesting to me.
2011-07-27 Matt Platte:
It's traditional to condescendingly break out the awk or sed one-liners here but for me, I get some joy - perverse joy, no doubt - in building something with the "wrong tools" that gets the job done anyhow. Good on you, Jezra
2011-07-28 jezra:
If it gets the job done, is it really the wrong tool? Possibly; but this way I'm learning ruby (which is the real goal)
2011-08-15 x1101:
@Jezra you're learning ruby to add another 'wrong' tool to your belt?
2011-08-15 jezra:
A tool is a tool; and the language is growing on me.
2011-09-15 Daniel Kullmann:
grep does that already (although not in green):

grep -ir --color=always pattern directory

the -r recurses through all subdirectories, and --color=always colorises the output.
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
8 minus 8
Answer:
required
subscribe
 
2016
2015
2014
2013
2012
2011
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008