views:

4196

answers:

7

I already know the obvious answer to this question: "just download <insert favorite windows grep or grep-like tool here>". However, I work in an environment with strict controls by the local IT staff as to what we're allowed to have on our computers. Suffice it to say: I have access to Perl on Windows XP. Here's a quick Perl script I came up with that does what I want, but I haven't figured up how to set up a batch file such that I can either pipe a command output into it, or pass a file (or list of files?) as an argument after the "expression to grep":

perl -n -e "print $_ if (m![expression]!);" [filename]

How do I write a batch script that I can do something like, for example:

dir | grep.bat mypattern
grep.bat mypattern myfile.txt

EDIT: Even though I marked another "answer", I wanted to give kudos to Ray Hayes answer, as it is really the "Windows Way" to do it, even if another answer is technically closer to what I wanted.

A: 

try renaming the executable to that of a known working application, iexplore.exe for example. This will probably get you round some of those application restrictions.

benPearce
that's not exactly true. A lot of solutions uses fullpath and hash to certify if the file is really allowed to run.
VP
+12  A: 

Most of the power of grep is already available on your machine in the Windows application FindStr.exe which is part of all Windows 2000, XP and Vista machines! It offers RegExpr etc.

Far easier than a batch file which in turn calls Perl!

c:\>FindStr /?    
Searches for strings in files.

FINDSTR [/B] [/E] [/L] [/R] [/S] [/I] [/X] [/V] [/N] [/M] [/O] [/P] [/F:file]
        [/C:string] [/G:file] [/D:dir list] [/A:color attributes] [/OFF[LINE]]
        strings [[drive:][path]filename[ ...]]

  /B         Matches pattern if at the beginning of a line.
  /E         Matches pattern if at the end of a line.
  /L         Uses search strings literally.
  /R         Uses search strings as regular expressions.
  /S         Searches for matching files in the current directory and all
             subdirectories.
  /I         Specifies that the search is not to be case-sensitive.
  /X         Prints lines that match exactly.
  /V         Prints only lines that do not contain a match.
  /N         Prints the line number before each line that matches.
  /M         Prints only the filename if a file contains a match.
  /O         Prints character offset before each matching line.
  /P         Skip files with non-printable characters.
  /OFF[LINE] Do not skip files with offline attribute set.
  /A:attr    Specifies color attribute with two hex digits. See "color /?"
  /F:file    Reads file list from the specified file(/ stands for console).
  /C:string  Uses specified string as a literal search string.
  /G:file    Gets search strings from the specified file(/ stands for console).
  /D:dir     Search a semicolon delimited list of directories
  strings    Text to be searched for.
  [drive:][path]filename
             Specifies a file or files to search.

Use spaces to separate multiple search strings unless the argument is prefixed
with /C.  For example, 'FINDSTR "hello there" x.y' searches for "hello" or
"there" in file x.y.  'FINDSTR /C:"hello there" x.y' searches for
"hello there" in file x.y.

Regular expression quick reference:
  .        Wildcard: any character
  *        Repeat: zero or more occurances of previous character or class
  ^        Line position: beginning of line
  $        Line position: end of line
  [class]  Character class: any one character in set
  [^class] Inverse class: any one character not in set
  [x-y]    Range: any characters within the specified range
  \x       Escape: literal use of metacharacter x
  \<xyz    Word position: beginning of word
  xyz\>    Word position: end of word
Ray Hayes
This is awesome! And great advice for a unix geek trying to "make it" on the Windows platform. Even if it didn't directly answer my question :-)
Ogre Psalm33
You can make a house out of bricks, but if you only have trees locally, make a cabin!
Ray Hayes
+1  A: 

You need to do something like this:

@echo off
perl -x -S script.pl %1

The "%1" will pass the argument to the perl script.

Save it as a .bat file and you're good to go.

iansinke
Would that work with a pipe? I mean if I use you bat file like this `gcc -MM files | yourbat regexp`. I tried this with `ack` and got unexpected behaviour.
Gauthier
+4  A: 

I wrote this a while back:

@rem = '--*-Perl-*--
@echo off
perl -x -S %0 %*
goto endofperl


@rem -- BEGIN PERL -- ';
#!d:/Perl/bin/perl.exe -w
#line 10
use strict; 
#use Test::Setup;
use Getopt::Long;

Getopt::Long::Configure ("bundling");

my $ignore_case    = 0;
my $number_line    = 0;
my $invert_results = 0;
my $verbose        = 0;

my $result = GetOptions( 
    'i|ignore_case' => \$ignore_case, 
    'n|number'      => \$number_line,
    'v|invert'      => \$invert_results,
    'verbose'       => \$verbose,
);
my $regex = shift;

if ( $ignore_case ) { 
    $regex = "(?i:$regex)";
}
$regex = qr/$regex/;
print "\$regex=$regex\n";
if ( $verbose ) { 
    print "Verbose: Ignoring case.\n"                      if $ignore_case;
    print "Verbose: Printing file name and line number.\n" if $number_line;
    print "Verbose: Inverting result set.\n"               if $invert_results;
    print "\n";
}

@ARGV = map { glob "$_" } @ARGV;

while ( <> ) { 
    my $matches = m/$regex/;
    next unless $matches ^ $invert_results;
    print "$ARGV\:$.:" if $number_line;
    print;
}

__END__
:endofperl
Axeman
This was the first "exact answer" response, and it looks like just what I was asking for. But, as mentioned, the "Windows Way" is using FINDSTR. However, I'll definitely keep this little script in my Windows Perl repertoire, and I'm sure this, or some variation of it, will come in handy at times!
Ogre Psalm33
Actually, I had a grep tool on my PC, but I wanted the power of Perl expressions for searching. FindStr is probably as good as grep, but this one doesn't search files, either. Ack, as Dave Webb mentioned, does both.
Axeman
+1  A: 

I agree with Axeman and Mr. Hayes about using a better tool for the job. That said, you could try something like this in your batch file to run your custom script against a file wildcard expression:

@echo off

for /f "usebackq delims==" %%f in (`dir /w /b %2`) do (
    perl -n -e "print $_ if (m!%1!);" "%%f"
    REM or something like:  myperlscript.pl %1 "%%f"
)

In this way, you can do things like "grep mypattern myfile.txt", "grep mypattern .", "grep mypattern *.doc", etc.

Jim Olsen
This is a useful tidbit. Through a small amount of searching, I've found precious little on the web about using arguments, piping, etc in batch files. This definitely turns on a light-bulb of understanding for me, thanks!
Ogre Psalm33
+11  A: 

Download and install ack. It's a superior replacement to grep and - thanks to Perl's magic dual mode .BAT / Perl script magic - it'll work on the command line for you.

hexten
Well, I did specify specifically that I have a strict environment on my PC of what I'm allowed to install. However, this may be a good option, as it looks to be a source-only download and my IT dept would probably let it slide. Good suggestion.
Ogre Psalm33
Yeah, I was going to suggest ack as well, but then I know that some IT departments are strict about even unauthorized Firefox plugins and Perl modules. So if you can write your own batch files, you can do this. but it looks like FindStr is a leap above DOS find.
Axeman
+5  A: 

First, turn it into a real script instead of a one-liner

use strict;
use warnings;

my $pattern = shift or die "Usage: $0 <pattern> [files|-]\n";
while (<>) { print if /$pattern/ }

Then turn it into a batch file using pl2bat:

pl2bat mygrep.pl

This will create "mygrep.bat"

For a full-featured grep (and many other Unix apps) written completely in Perl, see the Perl Power Tools project.

Edit: While the Perl Power Tools are good if you can only run Perl, I generally prefer the set of GnuWin32 tools. They don't require installation. (You don't need Admin privileges, just a directory you can write to.)

Michael Carman