Grep via Ruby

Hi,
I'd like to have a "grep" with case-insensitive match. normal grep does
not have this functionality (afaik), but ruby does. So this is, what i
wrote:

cat rgrep:
#!/usr/bin/env ruby
# Grep with full regexp-functionality via ruby

if ARGV.shift == "-p"
  pattern = Regexp.new(ARGV.shift)
else
  puts "Please give me a pattern with the '-p' option"
  exit
end
ARGV.each do |filename|
  File.open(filename) do |file|
    file.each do |line|
      puts "#{filename} #{file.lineno.to_s}: #{line}" if
pattern.match(line)
    end
  end
end

Using it via: rgrep -p '/delete /i' *.php does not match anything, but
this
#!/usr/bin/env ruby
# Grep with full regexp-functionality via ruby

if ARGV.shift == "-p"
  pattern = Regexp.new(ARGV.shift)
else
  puts "Please give me a pattern with the '-p' option"
  exit
end
ARGV.each do |filename|
  File.open(filename) do |file|
    file.each do |line|
      puts "#{filename} #{file.lineno.to_s}: #{line}" if /delete
/i.match(line)
    end
  end
end

DOES match. Does anyone see the bug? Maybe this can be done a lot
easier by using ARGF??

Thanks in advance
Ralf Müller

ralf wrote:

Hi,
I'd like to have a "grep" with case-insensitive match. normal grep
does not have this functionality (afaik), but ruby does.

14:14:49 [~]: grep -i foo <<EOF

foo
FOO
EOF

foo
FOO
14:14:58 [~]:

    robert

DOES match. Does anyone see the bug?

  /<something>/i is syntactic shugar for

Regexp.new(<something>, flag-for-case-insensitive-i-cannot-remember)

what you do is

Regexp.new("/<something>/i")

this is not what you want

···

--
Mit freundlichen Grüßen
Fritz Heinrichmeyer FernUniversität, LG ES, 58084 Hagen (Germany)
tel:+49 2331/987-1166 fax:987-355

ralf wrote:

I'd like to have a "grep" with case-insensitive match. normal grep does
not have this functionality (afaik), but ruby does.

Are you talking about Enumerable#grep or the grep command-line tool?

ralf wrote:

Hi,
I'd like to have a "grep" with case-insensitive match. normal grep does
not have this functionality (afaik), but ruby does. So this is, what i

Grep does have this functionality with the i switch.
grep -i

That being said it is ofcourse always fun to write your own script for
this :slight_smile:

···

--
Posted via http://www.ruby-forum.com/\.

I'd like to have a "grep" with case-insensitive match.

You can turn options on/off inside a pattern. For example, here is a pattern that matches the word file, regardless of case:

   (?i:file)

Maybe this can be done a lot easier by using ARGF??

Yes it can. Your input loop can be replaced with:

   ARGF.grep(pattern) do |line|
     puts "#{ARGF.filename} #{ARGF.lineno}: #{line}"
   end

Hope that helps.

James Edward Gray II

···

On Feb 7, 2006, at 7:08 AM, ralf wrote:

ralf wrote:

Hi,
I'd like to have a "grep" with case-insensitive match. normal grep does
not have this functionality (afaik), but ruby does. So this is, what i
wrote:

cat rgrep:
#!/usr/bin/env ruby
# Grep with full regexp-functionality via ruby

if ARGV.shift == "-p"
  pattern = Regexp.new(ARGV.shift)
else
  puts "Please give me a pattern with the '-p' option"
  exit
end
ARGV.each do |filename|
  File.open(filename) do |file|
    file.each do |line|
      puts "#{filename} #{file.lineno.to_s}: #{line}" if
pattern.match(line)
    end
  end
end

Using it via: rgrep -p '/delete /i' *.php does not match anything, but
this
#!/usr/bin/env ruby
# Grep with full regexp-functionality via ruby

if ARGV.shift == "-p"
  pattern = Regexp.new(ARGV.shift)
else
  puts "Please give me a pattern with the '-p' option"
  exit
end
ARGV.each do |filename|
  File.open(filename) do |file|
    file.each do |line|
      puts "#{filename} #{file.lineno.to_s}: #{line}" if /delete
/i.match(line)
    end
  end
end

DOES match. Does anyone see the bug? Maybe this can be done a lot
easier by using ARGF??

if ARGV.shift == "-p"
  pattern = Regexp.new(ARGV.shift,Regexp::IGNORECASE)
else
  puts "Please give me a pattern with the '-p' option"
  exit
end

puts "#{$FILENAME} #{$.}: #{$_}" if $_ =~ pattern while gets

I meant the command-line tool. I thought, that 'grep' should get the
'ignore-case' flag by the pattern like in ruby/perl and didn't thaught
about the command-line options. :((
Sorry for this so-damn-simple question.

regards
Ralf

Edwin van Leeuwen wrote:

That being said it is ofcourse always fun to write your own script for
this :slight_smile:

Heh, I wrote something similar the other day to search for stuff in the
most recent rails code base installed on a box.

Here is my version of grep.rb...

  require 'rake'
  FileList[ARGV[1..-1]].egrep(Regexp.new(ARGV[0], 'i'))

···

--
Posted via http://www.ruby-forum.com/\.

Minor suggestion, line is going to end in an nl anyway. I would use print, unless you want all that extra whitespace in the output.

···

On Feb 7, 2006, at 9:30 AM, James Edward Gray II wrote:

On Feb 7, 2006, at 7:08 AM, ralf wrote:

I'd like to have a "grep" with case-insensitive match.

You can turn options on/off inside a pattern. For example, here is a pattern that matches the word file, regardless of case:

  (?i:file)

Maybe this can be done a lot easier by using ARGF??

Yes it can. Your input loop can be replaced with:

  ARGF.grep(pattern) do |line|
    puts "#{ARGF.filename} #{ARGF.lineno}: #{line}"
  end

Hope that helps.

James Edward Gray II

puts() adds a newline character only if the string didn't already end in one.

James Edward Gray II

···

On Feb 7, 2006, at 1:13 PM, Logan Capaldo wrote:

Minor suggestion, line is going to end in an nl anyway. I would use print, unless you want all that extra whitespace in the output.

Here is a small script that I use for my source searches.

JS

* * * *

#!/usr/bin/env ruby

require 'getoptlong'

def generalized_search(ipat, xpat, base = '.', fpat = '*', recurse = true, lor = false)

   fns = []
   if recurse
     fns = Dir.glob("#{base}/**/#{fpat}")
   else
     fns = Dir.glob("#{base}/#{fpat}")
   end

   regexp = prepare_regexp(ipat, xpat, lor)

   results = []
   total_l = 0
   total_f = 0

   fns.each do |fn|
     next if File.directory?(fn)
     next unless `file #{fn}` =~ /text/
     ln_num = 0
     File.foreach(fn) do |line|
       ln_num += 1
       if eval regexp
         results << sprintf("%5d : %s", ln_num, line)
       end
     end
     if results.length > 0
       puts
       puts ">>> #{fn} : #{results.length} " + (results.length == 1 ? "match" : "matches")
       puts
       results.each { |ln| print ln }
       puts
       print ' ', '-' * 75, "\n"

       total_f += 1
       total_l += results.length
       results.clear
     end
   end

   puts
   puts "*** Found #{total_l} matching " + (total_l == 1 ? "line" : "lines") + " in #{total_f} " + (total_f == 1 ? "file." : "files.")
   puts

   total_f

end

def prepare_regexp(ipat, xpat, lor)
   regexp = []

   if ipat && ipat.length > 0
     ipat.each { |pat| regexp << "line =~ /#{pat}/i" }
   end

   ipattern = lor ? "(#{regexp.join(' or ')})" : "(#{regexp.join(' and ')})"

   regexp = []

   if xpat && xpat.length > 0
     xpat.each { |pat| regexp << "line !~ /#{pat}/i" }
   end

   xpattern = "(#{regexp.join(' and ')})"

   regexp = []
   regexp << ipattern if ipattern != '()'
   regexp << xpattern if xpattern != '()'
   "(#{regexp.join(' and ')})"
end

def print_help
   my_name = File.basename($0)

   puts
   puts " #{my_name} is a utility for quickly finding matching lines in multiple files."
   puts " Where #{my_name} is better than a simple grep is in accommodating multiple"
   puts ' include and exlude patterns in one go. Similar to egrep, the include and'
   puts ' exclude patterns can be regular expressions.'
   puts
   puts ' Usage:'
   puts
   puts " #{my_name} OPTIONS"
   puts
   puts ' where OPTIONS can be'
   puts ' --include | -i <include pattern>, can be specified multiple times,'
   puts ' --exclude | -x <exclude pattern>, can be specified multiple times,'
   puts ' --base | -b <search base directory>, can be specified only once;'
   puts ' if given multiple times, the last one survives;'
   puts ' defaults to current directory,'
   puts ' --filepat | -f <file pattern>, can be specified only once; if given'
   puts ' multiple times, the last one survives; defaults to *,'
   puts ' --recurse | -r <yes|no>, can be specified only once; if given multiple'
   puts ' times, the last one survives; defaults to yes,'
   puts ' --lor | -o logically OR the include patterns; default is to logically'
   puts ' AND them'
   puts ' --help | -h prints this message.'
   puts
end

def main

   ipat = Array.new
   xpat = Array.new
   base = '.'
   fpat = '*'
   rec = true
   lor = false

   if ARGV.index('--help') or ARGV.index('-h')
     print_help
     exit
   end

   begin
     opts = GetoptLong.new(['--include', '-i', GetoptLong::OPTIONAL_ARGUMENT],
                           ['--exclude', '-x', GetoptLong::OPTIONAL_ARGUMENT],
                           ['--base', '-b', GetoptLong::OPTIONAL_ARGUMENT],
                           ['--filepat', '-f', GetoptLong::OPTIONAL_ARGUMENT],
                           ['--recurse', '-r', GetoptLong::OPTIONAL_ARGUMENT],
                           ['--or', '-o', GetoptLong::NO_ARGUMENT ],
                           ['--help', '-h', GetoptLong::OPTIONAL_ARGUMENT]
                           )

     opts.each do |opt, arg|
       case opt
       when '--include', '-i'
         ipat << arg.to_s
       when '--exclude', '-x'
         xpat << arg.to_s
       when '--base', '-b'
         base = arg.to_s
       when '--filepat', '-f'
         fpat = arg.to_s
       when '--recurse', '-r'
         rec = arg.to_s.downcase == 'no' ? false : true
       when '--or', '-o'
         lor = true
       end
     end
   rescue Exception => e
     print_help
     exit
   end

   if ipat.length == 0 and xpat.length == 0
     puts
     puts '--- Hmm ... looks like you don\'t want to search for any pattern. Quitting!'
     puts '--- Try -h if you are looking for some help.'
     puts
     return
   end

   res = generalized_search(ipat, xpat, base, fpat, rec, lor)

end

if $0 == __FILE__
   main
end

ralf wrote:

I meant the command-line tool. I thought, that 'grep' should get the
'ignore-case' flag by the pattern like in ruby/perl and didn't thaught
about the command-line options. :((
Sorry for this so-damn-simple question.

Try:

  grep -i

And of course, see the manual page:

  man grep

Egads! Surely you jest!

% irb
irb(main):001:0> puts "Hello\n"
Hello
=> nil
irb(main):002:0> puts "Hello\n "
Hello
=> nil

You jesteth not. And to think this whole time I've been typing print when I could have been typing puts.

···

On Feb 7, 2006, at 2:39 PM, James Edward Gray II wrote:

On Feb 7, 2006, at 1:13 PM, Logan Capaldo wrote:

Minor suggestion, line is going to end in an nl anyway. I would use print, unless you want all that extra whitespace in the output.

puts() adds a newline character only if the string didn't already end in one.

James Edward Gray II