May 9th, 2008

How Can I Use Windows PowerShell to Report Back the Name and Creation Date for Files Containing a Specified String Value?

Hey, Scripting Guy! Question

Hey, Scripting Guy! Using Windows PowerShell, how can I search for a string value in all the files in a folder full of text files, then report back the file name and creation date for each file where that string value was found? Oh, and I’d like to have these files sorted by date, too.
— JS

SpacerHey, Scripting Guy! AnswerScript Center

Hey, JS. You know, the Scripting Guy who writes this column really misses baseball. Now, admittedly, he just wrote an entire article on that subject just yesterday. But that was different; yesterday he missed baseball because it’s possible that the Scripting Son has played his last meaningful baseball game ever. Today, however, he misses baseball for a much different, and much more important, reason: now that the season is over, the Scripting Son has decided that he and his dear old dad should go to the gym every night, run a few miles, and then lift weights for half an hour or so. (Baseball players typically don’t lift weights during the season.)

Oh. How … nice ….

In all fairness, it should be noted that the Scripting Guy who writes this column does go to the gym on a regular basis. However, while he always works up a good sweat, it tends to be a nice leisurely sweat: he rides the exercise bike or climbs the StairMaster, usually while watching Sports Center or Baseball Tonight. Does he ever run on the treadmill? No. Does he ever lift weights? No. Does he ever push himself to the brink of death? No.

Note. Of course, when you’re as old as the Scripting Guy who writes this column you’re pretty much on the brink of death at all times anyway.

In other words, the Scripting Guy who writes this column didn’t exactly have a fun-filled night last night. And to add insult to injury, the Scripting Son tricked his old man. The Scripting Guy who writes this column thought the two were going to the gym to play basketball; it was only after they got there that the Scripting Son said, “By the way, we’re going to run and lift weights tonight.”

Oh. How … nice ….

On the bright side, however, this has caused the Scripting Guy who writes this column to rededicate himself to his career; in fact, he has made a vow to immediately begin working harder, and to start putting in longer hours. Is that because working out makes him feel refreshed and invigorated? Well, no, not exactly; instead, he’s hoping that if he stays late each night the Scripting Son will get bored and go the gym without him.

And just to prove that he’s serious about working longer and harder, the Scripting Guy who writes this column even managed to write his column today (Oh. How … nice ….). Here you go, JS:

$colFiles = Get-ChildItem C:\Logs | Sort-Object CreationTime

foreach ($objFile in $colFiles) { $strMatch = Select-String -path $objFile -pattern “Failed” if ($strMatch) { (Write-Host $objFile.FullName $objFile.CreationTime) } }

Before we begin we should note that we have no doubt that a true Windows PowerShell aficionado could probably dispose of this problem with a single line of code (albeit one containing multiple commands and pipelines). To tell you the truth, we were too lazy – uh, too busy to try to come up with a one-line solution to the problem. Consequently, we went with a slightly less-elegant approach, but an approach that we knew would work. If anyone out there has another, more-elegant solution feel free to send it to us; we’d love to see what you did.

And if you can only send that late at night, after normal working hours, that’s fine; if he has to, the Scripting Guy who writes this column can stay late and wait for your email to arrive. Sure, he might miss his trip to the gym but duty calls, right?

As for the script that we came up with, we start out by using the Get-ChildItem cmdlet to retrieve a collection of all the files in the folder C:\Logs. As soon as we’ve grabbed all the files we immediately pipe the data to the Sort-Object cmdlet, asking Sort-Object to sort all the files by their CreationTime property. We store the sorted collection in a variable named $colFiles.

The $colFiles variable now contains a nice little collection of all the files in the folder C:\Logs, sorted by the file creation date. That’s all well and good, except for one thing: we don’t want a collection of all the files in the folder C:\Logs. Instead, we’re interested only in the files that include the target string Failed. What does that mean? That means we still have some work to do.

With that in mind, our next chore is to set up a foreach loop that allows us to loop through all the files in the collection; that’s what this line of code is for:

foreach ($objFile in $colFiles)

Inside this loop we use the Select-String cmdlet to search the first file in the collection for the string value Failed; any instances of that value that we find in the file will be stored in the variable $strMatch:

$strMatch = Select-String -path $objFile -pattern “Failed”

Still with us? Good, because – believe it or not – we’re almost done. How do we know whether or not this first file contains the string value Failed? That’s easy; we just check to see if $strMatch has a value of some kind:

if ($strMatch)

What if $strMatch doesn’t have a value? That’s no big deal; that simply means that the first file in the collection doesn’t contain the target text Failed. Consequently, we go back to the top of the loop and repeat the process with the next file in the collection.

Of course, things are a little different if $strMatch has a value; that means that the file in question does contain the target value Failed. But, again, no big deal; in that case, we simply echo back the value of the file’s FullName and CreationTime properties:

($objFile.FullName + ”       ” + $objFile.CreationTime)

From there it’s back to the top of the loop, where we try again with the next file in the collection. By the time we’ve finished looping through all the files in C:\Logs we should see output similar to this onscreen:

C:\Scripts\test.txt 2/15/2008 7:04:29 PM
C:\Scripts\z.txt 3/10/2008 9:05:15 AM
C:\Scripts\mylog.txt 4/18/2008 12:54:47 PM
C:\Scripts\testlog.log 1 4/18/2008 1:03:26 PM
C:\Scripts\testfile.csv 4/18/2008 1:06:03 PM

Not the prettiest output in the world, but you get the idea.

Incidentally, the one complicating factor here – at least for the Scripting Guys – was the need to echo back the file creation date along with the file name. Suppose all we cared about was the name of each file where the target text was found. In that case, this single line of code would do the trick:

Select-String -path “C:\Logs\*.*” -pattern “Failed” -list | Select-Object Path

What we’re doing here is asking Select-String to look at each and every file in the folder C:\Logs and see if it can find the word Failed. In addition, we’ve tacked on the –list parameter, which tells Select-String that we only want to find the first match in each file. What if we left this parameter off? Well, suppose that the file Test.txt contained 10 instances of the word Failed. In that case, we’d get back 10 instances of Text.txt, one for each match.

Oh, and if all we did was call Select-String by itself without piping it to Select-Object we’d get output similar to this:

C:\Scripts\mylog.txt:2:Failed
C:\Scripts\test.txt:13:Failed
C:\Scripts\testfile.csv:2:Failed
C:\Scripts\testlog.log:2:Failed
C:\Scripts\z.txt1:7:Failed

As you can see, that’s actually more information than we need; it includes not only the file Path, but also the line number where the target text was found (e.g., 2) and the target text itself. We can get rid of the line number and the target text by piping the output to Select-Object, and asking Select-Object to filter out everything except the Path property. Now our output should look like this:

C:\Scripts\mylog.txt
C:\Scripts\test.txt
C:\Scripts\testfile.csv
C:\Scripts\testlog.log
C:\Scripts\z.txt

Very nice, but, as you can see, no file creation date; that’s because the Select-String cmdlet doesn’t retrieve the file creation date. In turn, that’s why we kicked things off by using Get-ChildItem; Get-ChildItem knows everything there is to know about file creation dates.

We hope that answers your question, JS. Listen, as long as you’re here, you don’t happen to need an 18-year-old first baseman/pitcher do you? No, no reason for asking. We were just wondering.

Author

0 comments

Discussion are closed.