45

The title speaks for itself really. I only want to know if it exists, not where it is. Is there a one liner to achieve this?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
ChrisInCambo
  • 8,455
  • 15
  • 50
  • 63
  • sorry, I like your question, but, please, rename it. I was catched in `detect` function speciality (return first element, not all, uh) – gaussblurinc Mar 25 '14 at 07:37

5 Answers5

52
File.open(filename).grep(/string/)

This loads the whole file into memory (slurps the file). You should avoid file slurping when dealing with large files. That means loading one line at a time, instead of the whole file.

File.foreach(filename).grep(/string/)

It's good practice to clean up after yourself rather than letting the garbage collector handle it at some point. This is more important if your program is long-lived and not just some quick script. Using a code block ensures that the File object is closed when the block terminates.

File.foreach(filename) do |file|
  file.grep(/string/)
end
builder-7000
  • 7,131
  • 3
  • 19
  • 43
AdamK
  • 1,169
  • 1
  • 8
  • 9
  • 46
    This actually leaks a file descriptor which could cause a serious problem. Here's a cleaner alternative that will close the file after it's used. `open('some.txt') { |f| f.grep(/string/) }` – Blake Taylor Jun 04 '12 at 00:27
  • 14
    And also leads to load the whole file in memory. `open("some.txt") { |f| f.each_line.detect { |line| /re/.match(line) } }` – rewritten Dec 09 '13 at 10:27
  • 2
    -1 because for some reason it returns false when it should be true. Investigating why. – kgpdeveloper Apr 21 '14 at 06:03
  • 2
    Guys, I see no reason why File#grep would drag file into memory. Can you show me smth about that in docs? – Nakilon May 24 '16 at 17:11
  • I would give priority to multi-line queries over worrying about running out of memory when loading a file for parsing. If you're parsing large files on small/embedded systems, you've got a whole host of optimization concerns that fall outside typical parsing requirements. I would assume what you're parsing for is fine per-line is if I had intrinsic information to confirm it, but only then. – kayleeFrye_onDeck Jun 05 '17 at 19:59
  • This code of yours does not leak file descriptors: `File.foreach(filename).grep(/string/)`. The file gets opened and closed within the call to `grep`. If you don't believe it, try printing some messages immediately before and after `grep` and use `strace ./your_program.rb` to see what happens between those two prints. – David Grayson Feb 14 '18 at 07:07
  • I have not found specifications for a File.grep or String.grep method. What version of ruby is this? – Julien Lamarche Feb 07 '19 at 19:27
  • @JulienLamarche `IO::foreach` and `Enumerable#grep` - https://ruby-doc.org/core-2.5.0/IO.html#method-c-foreach - https://ruby-doc.org/core-2.5.0/Enumerable.html#method-i-grep – go2null Apr 28 '19 at 21:08
  • Isn't "slurping" actually the complete opposite? Meaning, reading the entire file in memory. – Iulian Onofrei Jan 13 '20 at 16:00
  • What if I don't have line breaks in my big file? – EugZol May 14 '20 at 12:40
10

grep for foo OR bar OR baz, stolen from ruby1line.txt.

$  ruby -pe 'next unless $_ =~ /(foo|bar|baz)/' < file.txt
Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
7

If your OS has a grep package, you could use a system call:

system("grep meow cat_sounds.txt")

This will return true if grep returns anything, false if it does not.

If you find yourself on a system with grep, you may find this is the "best" way because Ruby can be slow when it comes to file operations.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
3

Well it seems eed3si9n has the one liner down, here's the longer solution:

f = File.new("file.txt")
text = f.read
if text =~ /string/ then
#relevant code
end
John T
  • 23,735
  • 11
  • 56
  • 82
1

This reads the file only to the first appearance of 'string' and processes it line by line - not reading the whole file at once.

def file_contains_regexp?(filename,regexp)
  File.foreach(filename) do |line|
    return true if line =~ regexp
  end
  return false
end
Tilo
  • 33,354
  • 5
  • 79
  • 106