Bratish Goswami

Personal website and blog

Was trying to search through files in Ruby and needed to get line numbers along with the matches, similar to grep -n. The grep -n command tells grep to display the line numbers along with each matching line when searching for patterns in a file or standard input.

Turns out there are several ways to do this in Ruby.

Method 1: Using File.readlines with each_with_index

def grep_with_line_numbers(filename, pattern)
  File.readlines(filename).each_with_index do |line, index|
    puts "#{index + 1}: #{line}" if line.match(pattern)
  end
end

grep_with_line_numbers("myfile.txt", /error/)

Method 2: Using File.open with line numbers

def grep_with_line_numbers(filename, pattern)
  File.open(filename, 'r') do |file|
    line_num = 0
    file.each_line do |line|
      line_num += 1
      puts "#{line_num}: #{line}" if line.match(pattern)
    end
  end
end

Method 3: One-liner approach

File.readlines("myfile.txt").each_with_index { |line, i| puts "#{i+1}: #{line}" if line =~ /pattern/ }

Method 4: More advanced with multiple files

def grep_files(pattern, *files)
  files.each do |filename|
    File.readlines(filename).each_with_index do |line, index|
      if line.match(pattern)
        puts "#{filename}:#{index + 1}: #{line.strip}"
      end
    end
  end
end

grep_files(/error/, "log1.txt", "log2.txt", "log3.txt")

Method 5: Using IO.foreach (memory efficient)

def grep_with_line_numbers(filename, pattern)
  line_num = 0
  IO.foreach(filename) do |line|
    line_num += 1
    puts "#{line_num}: #{line}" if line.match(pattern)
  end
end

Method 6: Case-insensitive search

def grep_case_insensitive(filename, pattern)
  File.readlines(filename).each_with_index do |line, index|
    if line.match(pattern, Regexp::IGNORECASE)
      puts "#{index + 1}: #{line.strip}"
    end
  end
end

Method 7: Return results instead of printing

def grep_with_line_numbers(filename, pattern)
  results = []
  File.readlines(filename).each_with_index do |line, index|
    if line.match(pattern)
      results << { line_number: index + 1, content: line.strip }
    end
  end
  results
end

matches = grep_with_line_numbers("myfile.txt", /error/)
matches.each { |match| puts "Line #{match[:line_number]}: #{match[:content]}" }

For large files, use IO.foreach:

def grep_large_file(filename, pattern)
  line_num = 0
  IO.foreach(filename) do |line|
    line_num += 1
    yield line_num, line if line.match(pattern)
  end
end

grep_large_file("huge_log.txt", /error/) do |line_num, line|
  puts "#{line_num}: #{line.strip}"
end

Method 8: Using grep command from Ruby

def system_grep(filename, pattern)
  result = `grep -n "#{pattern}" #{filename}`
  result.split("\n").each { |line| puts line }
end

Method 9: Multiple patterns

def multi_grep(filename, patterns)
  File.readlines(filename).each_with_index do |line, index|
    patterns.each do |pattern|
      if line.match(pattern)
        puts "#{index + 1}: #{line.strip} (matched: #{pattern})"
        break
      end
    end
  end
end

multi_grep("log.txt", [/error/i, /warning/i, /exception/i])

The IO.foreach approach is best for large files since it doesn't load everything into memory at once. For even better performance with large files, consider using the system grep command directly.