# Run a block conditionally on grep output

I've found a number of solutions to this problem, but unfortunately none of them seem to work on my windows box.

I'm running a FOR loop in a batch file to loop through a number of text files. For each text file, I want to ECHO its name (and possibly redirect output to file) if a grep (from WinGNU) returns a line. A quick google search shows me to do it this way:

IF grep -c pattern file ( stuff )


However this throws an error

-c was unexpected at this time.


Alternatively, I figured I could use the exit code from grep --quiet like so:

grep -q pattern file
IF ERRORLEVEL 1 ( stuff )


This always fails the IF test, even when the grep succeeds.

Is there a good way to run a conditional statement in Windows if my grep finds a match?

My code is:

SET locations=( a, long, list, of, last, two, octets )
SETLOCAL ENABLEDELAYEDEXPANSION

FOR %%r IN %locations% DO (
grep -i "^$CardSwipe$" \\10.0.%%r\path\to\file.ini
IF ERRORLEVEL 1 echo %%r


My output is:

[this line left intentionally blank] :)

• Which version of Windows? If Vista or later, PowerShell is probably better suited. – Iszi Jul 30 '14 at 23:24
• @Iszi win7, but would prefer batch to PowerShell since I'd have to learn the latter. That said: this may have to be my final push to learn PowerShell :) – Adam Smith Jul 30 '14 at 23:35
• And I should be learning RegEx. Could you parse that grep for me? Particularly, what sorts of strings are you trying to match? – Iszi Jul 31 '14 at 0:26
• @Iszi a line that begins with a literal [CardSwipe] case insensitive. I'm scanning a set of ini files that should have [CardSwipe] commented out, so I need to know if any of them still begin with [CardSwipe] (as opposed to hidden behind the rem e.g. ;[CardSwipe] – Adam Smith Jul 31 '14 at 0:34
• Ok. I think I've got a PowerShell fix for you. – Iszi Jul 31 '14 at 0:35

I can't think of a good way to do this with Batch, especially since you're bringing in non-native command-line tools that I'm not familiar with. However, PowerShell is generally much more well-suited for tasks like this.

This should do the job for you in PowerShell:

$Hosts = @('1.1','1.2','101.243','54.28')$Hosts | ForEach-Object {
if ((Get-Content "\\10.0.$_\path\to\file.ini" | Select-String "^$CardSwipe$").length -ne 0) { Write-Output$_}}


Here's the breakdown:

1. We define $Hosts as an array containing strings which represent the last two octets of each system to be checked. An alternate method of setting this up would be to have a text file with one entry per line. 2. Then we pipe the array into ForEach-Object which, as the name implies, will execute commands for each object in the array. If you use a text file as your list instead, you'll need to fetch it with Get-Content and then pipe it into ForEach-Object in place of the variable. 3. The script in the if condition is what's going to do your hunting for you. 4. Get-Content does what it says on the tin - gets the content of stuff. 5. "\\10.0.$_\path\to\file.ini" is the target we're giving Get-Content. By wrapping the string in double-quotes, we're telling PowerShell that we want it to expand variables within the string before passing the parameter on. \$_ is a built-in variable which references the current object being worked on by things like ForEach-Object.
6. The output from Get-Content is piped to Select-String. Select-String is more or less grep for PowerShell - in fact, I'm a little surprised there isn't a built-in alias for this one since other cmdlets (what they call PowerShell commands) have aliases to similar *nix commands like ls and wget.
7. "^$CardSwipe$" - Oh, yeah. Select-String takes RegEx by the way.
8. Wrapping commands in parenthesis allows you to directly retrieve properties of the objects they generate, without having to first dump them to variables or something else. So, ( ... ).length means that I want to get the length of whatever object is output from the script within the parenthesis. For strings, this is basic character length. For arrays, this is the number of members in the array. When a text file or similar object is retrieved, it's returned as an "array of strings". So, the value of the object's length property will be equal to the number of lines in the text.
9. -ne is the comparison operator for "not equal to". If Select-String doesn't find any matches, its output will be a null array with length zero. Anything else will have length 1 or more, so we trigger the if on -ne 0.
10. Write-Output is another one of those self-explanatory commands. It writes output. In this case, we're telling it we want to output the current object being processed by ForEach-Object. Of course, since this is within the if block, it will only happen when the ini file matches the given conditions. The output will be whichever "last two octets" were being processed when that happened.
• Cool, thanks. I'll have to do a bit of looking at string manipulation in powershell. In batch I was doing set current=%%r; set deviceinfo=site: %current:~-5,-4% device: %current:~-1% – Adam Smith Jul 31 '14 at 4:01
• Alas, I WAS still holding out hope for an answer in batch rather than powershell. However, I appreciate your work and thanks for writing out the detailed explanation. Since I'm going to have to implement this in Powershell, I've gotta do some homework and your detailed explanation can only help with that! Thanks again. – Adam Smith Aug 5 '14 at 23:55
• @AdamSmith I might be able to help fill in the missing pieces, but I'm not exactly sure what that batch bit in your comment does. If there's anything else you need help with, you can always search here or ask a powershell question. – Iszi Aug 6 '14 at 0:33