tags:

views:

427

answers:

8

My question is probably simple but I'm a complete newbie. I want to search the contents of multiple text files for a particular phrase and then display the lines of the finds on screen. I've already learnt how to deal with a single file. For example, if I want to search for a word, say "Okay" in a text file named "wyvern.txt" in the root directory of F. The following code works:

#!/usr/bin/perl

$file = 'F:\wyvern.txt';
open(txt, $file);
while($line = <txt>) {
  print "$line" if $line =~ /Okay/;
}
close(txt);

But what should I do if I want to search for the same phrase in two text files, say "wyvern' and "casanova" respectively? or how about all the files in the directory "novels" in the root directory of F.

Any help would be greatly appreciated. Thanks in advance

Mike

Edit:

Haha, I finally figured out how to search all the files in a directory for a pattern match:) The following code works great:

#!/usr/bin/perl  
@files = <F:/novels/*>;
foreach $file (@files) {
open   (FILE, "$file");
while($line= <FILE> ){
print "$line" if $line =~ /Okay/;
}
close FILE; 
}
+3  A: 

The easiest way is to list the files on the command line, and then simply use:

while (<>)
{
    print if m/Okay/;
}
Jonathan Leffler
+1  A: 

put the files in a for loop, or something along those lines:

i.e.

for $file ('F:\wyvern.txt','F:\casanova.txt') {

open(TXT, $file);
while($line = <txt>) {
     print "$line" if $line =~ /Okay/;
    }
close TXT;
}
Lazy Bob
@Lazy Bob, I changed ($line = <txt>) to ($line = <TXT>) and retested the code again. It works great. Thanks for sharing the code.
Mike
+2  A: 

File::Grep is what you need here

ennuikiller
+3  A: 

Extending the good answer provided by Jonathan Leffler:

The filename where the match was found is in $ARGV, and with a small change, the line number can be found in $.. Example:

while (<>) {
  print "$ARGV:$.:$_" if /pattern/;
} continue {
  close ARGV if eof; # Reset $. at the end of each file.
}

Furthermore, if you have a list of filenames and they're not on the commandline, you can still get the magic ARGV behavior. Watch:

{
  local @ARGV = ('one.txt', 'two.txt');
  while (<>) {
    print "$ARGV:$.:$_" if /Okay/;
  } continue {
    close ARGV if eof;
  }
}

Which is a generally useful pattern for doing line-by-line processing on a series of files, whatever it is -- even if I might recommend File::Grep or App::Ack for this specific problem :)

hobbs
Thank you hobbs! The code works perfectly and now I can search multiple files for a specified string :)---------------#!/usr/bin/perl{ local @ARGV = ('F:\wyvern.txt', 'F:\casanova.txt'); while (<>) { print "$ARGV:$.:$_" if /Okay/; } continue { close ARGV if eof; }}------------------------But what should I do if I want to search all the files in a specified directory, say "novels" in the drive F?
Mike
Look at `glob()` to get files in one directory, and `File::Find::Object` or `File::Find::Rule` to find files in subdirectories.
hobbs
Thank you, hobbs! Things seem to be getting interesting for a beginner like me:)
Mike
globbing works!
Mike
A: 

There is a mistake in my code. Correct one should be:

#!/usr/bin/perl
$file = 'F:\wyvern.txt';
open(txt, $file);
while($line = <txt>) {
  print "$line" if $line =~ /Okay/;
}
close(txt);

I started learning Perl just yesterday and I'm running Perl on Windows XP. I have no experience in any other programming languages. The above mentioned code is a modified version of an example from a book titled "Perl by Example".

Thanks to Lazy Bob, but the code does not seem to work. Nothing happens. Any idea why?

#!/usr/bin/perl
for $file ('F:\wyvern.txt','F:\casanova.txt') {
open(TXT, $file);
while($line = <txt>) {
     print "$line" if $line =~ /Okay/;
    }
close TXT;
}

Thanks to Jonathan Leffler, I'm trying to figure out how to list the files. Thanks to ennuikiller, I'm trying to figure out how to modify the code for my use.

Mike
Ideally, you should edit your question, rather than supply comments as an answer.
Jonathan Leffler
How to list the files: if your script is 'perlgrep', then you'd type: `perlgrep F:\wyvern.txt F:\casanova.txt F:\casablanca.txt` to list three files, which would all be processed by the code I showed. @Hobbs provided you some worthwhile extensions to what I gave, especially if you want to print file names or line numbers as well as the matching text.
Jonathan Leffler
Also, Perl is case sensitive: using 'open(TXT, $file)' and then `while ($line = <txt>)` is a mistake; you need TXT in the angle brackets.
Jonathan Leffler
Thank you for your kind reminder and nice suggestions :) It was silly of me. I wasn't quite aware that I could edit my postings.
Mike
Well, for the TXT mistake, the corrected code is still not working. #!/usr/bin/perlfor $file ('F:\wyvern.txt','F:\casanova.txt') {open(TXT, $file);while($line = <TXT>) { print "$line" if $line =~ /Okay/; }close TXT;}
Mike
@Jonathan, I didn't know Perl is case sensitive and I didn't know how to set things right. Now I looked at Lazy Bob's code and changed the filehandle and it works. And then I found your comment and then I found my own reply to your comment. I guess there must have been something wrong with the config. Anyway now it works and thanks for the guidance.
Mike
A: 

Okay, I'm a complete dummie. But to sum up, I now can search one single text file or multiple text files for a specified string. I'm still trying to figuring out how to deal with all the files in one folder. the following codes work.

Code 1:

#!/usr/bin/perl
$file = 'F:\one.txt';
open(txt, $file);
while($line = <txt>) {
  print "$line" if $line =~ /Okay/;
}
close(txt);


Code 2:

#!/usr/bin/perl
{
  local @ARGV = ('F:\wyvern.txt', 'F:\casanova.txt');
  while (<>) {
    print "$ARGV:$.:$_" if /Okay/;
  } continue {
    close ARGV if eof;
  }
}


Thanks again for your help. I really appreciate it.

Mike
+1  A: 

On a system where command line arguments are properly expanded, you can use:

[sinan@host:~/test]$ perl -ne 'print "$.:$_" if /config/' *
1:$(srcdir)/config/override.m4

The problem with Windows is:

C:\Temp> perl -ne "print if /perl/" *.txt
Can't open *.txt: Invalid argument.

On Windows, you could do:

C:\Temp> for %f in (*.txt) do perl -ne "print if /perl/" %f

But, you might just want to use cmd.exe builtin findstr or the grep command line tool.

Sinan Ünür
+1  A: 

Just a tweak on your line: <F:/novels/*>, I prefer to use the glob keyword - it works the same in this context and avoids the chances of confusing the many different uses of angle brackets in perl. Ie:

@files = glob "F:/novels/*";

See perldoc glob for more.

Mark Aufflick
Thanks for the tips.
Mike