Question

I want to look for a string in all the files in a directory in perl. But the following code is not working

if ($^O eq "MSWin32") {
my @results = `findstr /s "Hello" /d:\\Testing`;
print "my results \n @results\n";
#or 
system('findstr /s "Hello" /d:\\Testing');
}

I am running the script from "C:" and it contains "Testing" directory. For the non windows system it works perfectly fine using grep system command. Please help. Thanks

Was it helpful?

Solution

You're not using findstr correctly.

  1. The order matters: switches/options should come before the argument (search string) (see output of findstr /?)
  2. The argument which specifies which file(s) to search is a required argument (this argument allows wildcard characters).

With all those in mind, a working findstr command would be

findstr /s /d:"Testing" *

If you just need the filenames containing matches (not the content of line in the file with the match), use the /m switch

findstr /s /m /d:"Testing" *

Also the /s switch is not needed if you don't need recursive searching (e.g. search the subdirectory of C:\Testing)

Another thing to note is that if you use /d:"Testing", the first line of the output would be

Testing:

which would end up as the first element in your array. To counter this, you can shift your array, or better still, change the working directory to Testing in Perl.

The code snippet below shows how I would rewrite this

#!/usr/bin/env perl

use strict;
use warnings;

if ($^O eq "MSWin32") {
    chdir("Testing");
    my @results = qx'findstr /s /m "Hello" *';
    local $" = ", ";
    @results = map { s/^/Testing\\/; chomp; $_ } @results;
    chdir("..");
    print "my results \n@results\n";
}

Other notes (god, this post is too long already!):

  • qx'...' is somewhat equivalent to `...` except that whatever is in between is not interpolated.
  • $" is the separator used for array elements when the array is interpolated within double quotes in a print or say (once you get the code working, change its value to see what it does)
  • The map does some processing on each element of the array
    • s/^/Testing\\/ prefixes each filename with "Testing**" (so that your results will be **relative to C: instead of relative to Testing)
    • chomp removes trailing whitespace character (e.g. newline) from the result
    • The last $_ makes sure that it returns the modified filename (instead of the return value of s/// which is # substitution)

You can go one step further and parameterize the folder name ("Testing") and string-to-search in the code, and even get user input for those parameters. But that is left as an exercise to the reader (because I'm le tired and the post is already too long)

OTHER TIPS

Judging by the findstr documentation, you appear to have your findstr options a bit mixed up; /d doesn't take an argument, and it looks like switches are required to appear before the search string and the start path. So try this:

findstr /d /s "Hello" \\Testing

in place of the findstr command you're currently using, and you may find better results.

I think you might want to try http://search.cpan.org/~mneylon/File-Grep-0.02/Grep.pm

Also, when writing functions cross platform for Linux and Windows, I tend to include this at the top of my scripts just to cover anything I may do that is windows specific:

BEGIN { # include additional Win32 specific modules when running on MS platforms
  if ($^O =~ /Win32/) {
    require Win32::AdminMisc; import Win32::AdminMisc;
    require Win32::API; import Win32::API;
    require Win32::GUI; import Win32::GUI;
    $wincon=Win32::GUI::GetPerlWindow();
  }
}

# your code starts here
findstr /s "Hello" /d:\\Testing

specifies a UNC path, i.e., looking for files on a remote host named Testing. Use a single backslash to specify a directory. But I'm not sure that that's what you want either

findstr /s "Hello" /d:\Testing

specifies the directory Testing at the root of the current drive, which is to say C:\Testing. If you want to specify the directory Testing under the current directory, then it's

findstr /s "Hello" /d:Testing

and if you're also finding findstr to be pretty tempermental, you may have better luck with something like

findstr /s "Hello" /d:Testing\*

But of course, the real solution is to install the DWIM tool ack, your inability to use CPAN libraries notwithstanding.

Your invocation of findstr is hanging because you did not provide a glob pattern to match against.

Consider using code similar to

if ($^O eq "MSWin32") {
  my @results = `findstr /s /p /d:C:\\Testing "Hello" *.*`;
  die "$0: findstr failed" if $?;

  if (@results) {
    print @results;
  }
  else {
    warn "$0: no results\n";
  }
}

Notes:

  • The big change is the addition of *.* at the end of the command.
  • Check that findstr ran successfully by examining $? rather than blindly assuming success.
  • The /p option tells findstr to skip files with unprintable characters. You may or may not want to retain this option.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top