Summary: Windows PowerShell superhero BATCHman uses regular expressions to parse output from handle.
Microsoft Scripting Guy Ed Wilson here. Today we have Episode 10 of the BATCHman series.
Whenever trouble happens in systems and people will call,
and darkness rolls out causing your fall,
Creatures of bits roam in the night,
Shine to the sky, the bright bluish light,
and call to…BATCHman !
…and, oh yes, his sidekick Boy Blunder Cmdlet, too.
A smell rolls out from the darkness. An evil smell.
“Don’t look at me!” Cmdlet looks defensively “I didn’t do it!”
The BATCHman looks intensely at his “BATCH-o-Meter for Evil Detecting.” “No, little buddy, it’s the smell of evil afoot. Somewhere in this city, a crime against infrastructure is occurring. I can feel it, I can smell it…”
***Meanwhile, in a far smellier section of the city, evil is afoot at the Redmond Pocket Protector Recycling Plant.***
“Our backup has failed again! Why??!”
Jane the local IT pro/accountant/phone guru/everything else stares at the logs. “More than 3,000 files not backed up again! All Word and Excel documents! Why?!!”
In the distance, somewhere within the shadows, a mad cackle can be heard. The sound of a shrieking creature, perhaps a Banshee or just a squirrel that listened to a bit too much disco music. But a horrid sound echoed throughout the building.
“Your files are all MINE! MIIIIIIINNNNNEEEE!!! Your backup will never work again! Hee-hee-hee-hee!” the sound echoed ominously.
Out of the corner of her eye, Jane spotted a shadow slither into the darkness. Immediately, she knew what to do. Grabbing her smart phone and popping open Twitter, she pinged the one person that could help her in this dire hour.
***Meanwhile, in a far less smelly section of the city, evil is not afoot.***
Sounds of a budgie echoed throughout the BATCHcave.
“Holy shrieking parakeets, BATCHman! It’s the BATCHtweet account!”
Quickly popping open a module in the BATCHbelt, BATCHman checked for his updates.
“No, I don’t want a million dollars. No, I don’t want a free crate of Tang. Cmdlet! I was right! There was evil afoot and I’m not talking about what you had for lunch! Look.”
@JaneITGuru @Batchman! Quick! Help! Backups failing daily! Too many open files! Shrieking! Help me BATCHman!
“Looks like that new Twitter account paid off, BATCHman. Glad I suggested it!”
“Yes Cmdlet! Quickly! To the BATCHcycles!”
Cmdlet stared at the blue tricycle with “Cmdlet“ written on the side in permanent marker. “Must we?”
“We’re a green organization here!” cried BATCHman as he mounted his 40-speed carbon fiber, midnight blue special “Quickly! Crime waits for no-one!”
***Moments later, Cmdlet is huffing and puffing.***
“Huff…puff…no more…*gasp*…Double Downs for…*ack*…breakfast!” gasped Cmdlet between breaths.
Jane looked out to greet BATCHman and the heavily sweating Cmdlet at the door. “Oh, thank goodness you’re here! None of our backups are working on the file server! It’s failing because there are documents open when they shouldn’t be! Plus there’s this noise.”
BATCHman steps off, listening keenly to the air. The shrieking cackling sound could be heard ominously throughout the building.
“I’d know that sound anywhere.” BATCHman shook his head. “That’s the Tapeworm! I remember this criminal from an article in BadGuys Weekly.”
“The Tapeworm?” Jane queried.
“Yes. He was once a brilliant IT professional, but his mind went and snapped one day. He was manning the backups and spent twenty hours restoring an email database from QIC-20 tapes, only to find it was because somebody wanted a cat picture. Since that day, Tapeworm attaches himself to file systems and ruins backups.”
“Holy, Hello Kitty!” burst out Cmdlet “I had no idea cats were so evil!”
“Yes, so what is happening here. I heard ‘failed backups.’”
Jane gestured to the backup logs indicating failures due to open files.
“We’ve even tried selecting the open files in Computer Management, but it takes far too long to enumerate! Plus massive piles of different documents are reopened daily before the backup.”
“BATCHman!” burst out Cmdlet “We’ve got to help her get a handle on this!”
“Yes,” the Tilley-hatted one muttered. “Funny you should mention ‘Handle’. That’s the perfect answer.”
Poor Cmdlet stared at the floor confused. Deciding to practice telekinesis on his shoelaces was more productive. “I don’t get it,” was all he muttered.
“Handle as in Handle.exe from Sysinternals. We could use that to close the files!”
Cmdlet groaned in dismay as yet another horrible joke left BATCHman’s lips. “Holy PUNishment, BATCHman—that was horrible!”
Our hero smiled with a sly grin. Another well-played play on words.
Jane looked up. “But BATCHman. I already thought of that. We need something that can be automated. That application can’t be automated. Normally you run it, it shows the open file Handles in HEX with the process numbers, and then you manually run it again like this to close a file handle.”
HANDLE.EXE –c 2a3 –p 5342
Our hero stood up proudly. “For ordinary people and mere mortals, no. But for us, the kings of automation, we have…” he paused ever so dramatically while pulling out a megaphone “WINDOWS POWERSHELL!”
While rubbing their ears, they looked at BATCHman equally puzzled. “Automate it how?”
BATCHman looked over. “Remember the first rule of Windows PowerShell, which is…”
“Ooooh! Ooooooooo!” Cmdlet jumped up and down like a gerbil on a hot coal “I know! Everything is an object!”
“Exactly! If I ran Handle while in Windows PowerShell and got some output like this…”
”...we’d notice there is a consistent pattern of well-formatted columns. So what we could do is store the output in a Windows PowerShell variable to play with it like this.”
$ScreenOutput=.\HANDLE.EXE DOCX
Cmdlet stared at the screen with Jane then burst out. “HOLY PATTERN BUFFER BATCHman! Every line has ‘PID:’ before the Process ID and the word ‘File’ with a pile of blank spaces before the File Handle! If we could somehow pull out the numbers after PID and FILE we’d be halfway there. But we need to clean all the extra stuff out too, like the title.”
BATCHman looked at his sidekick. “So what do you think we could do to simplify this output?”
Cmdlet rubbed his hands eagerly at the thought of solving the puzzle. “I’ll wager we could run this output through Select-String to pull only the lines that have the pid: field in it!”
$ScreenOutput | SELECT-STRING –pattern ‘pid: ’
Cmdlet began to dance a jig like a leprechaun who’d gotten away with his box of Lucky Charms. “W0000000t! Aha!”
“That’s good but we can go one step better. With the \w from regular expressions, we can tell the string to find us all letters up to but not counting spaces. We’ll add an asterisk, which means all characters following that match this pattern. We can store away the results in a variable to make it easier to work and manipulate.”
$ProcessIDResults=$ScreenOutput | SELECT-STRING –pattern ‘pid: [\w]*’
Cmdlet thought. “So we could run a similar search for lines beginning with File, but how do we specify to find all data after that up until the colon character that follows the file Handle?
BATCHman explained. “In regular expressions, you can also specify the pattern of \s\S, which means accept any character including spaces. Doing this we can pull down only the data we need. We can tell it to search for all data beginning with File and ending with a colon, like this.”
$FileHandleResults=$ScreenOutput | SELECT-STRING –pattern ‘File [s\S]*?:’
Cmdlet looked. “So regular expressions can be used in many situations, BATCHman, but how does this help us? We need to get this information back to the Handle program!”
“Never fear, little chum. Let’s see which methods and properties we have available with a Get-Member.”
$ProcessIDResults | GET-MEMBER
“As you can see, like when we battled Dr. Regex, there is a Matches property. Let’s take a look at the data in one of those."
$ProcessIDResults[0].matches[0]
“And of course, if we run Get-Member against this, we can see which methods and properties are available on this object.”
$ProcessIDResults[0].matches[0] | GET-MEMBER
Cmdlet’s eyes lit up when he saw tostring() as a method. He knew from his previous experiences with string objects in Windows PowerShell there was a substring() method available.
“BATCHman! We could convert each match to a string! And because we know the data is from the Matches object, we could run a substring() on the output!” Cmdlet barked out like a chihuahua.
Before BATCHman could reply, a loud blaring klaxon went off. Flashing neon lights lit the city streets!
“Oh, no!” burst out BATCHman. “That’s the alarm on the WinMobile! Somebody’s gotten to it!” as he barreled down the stairs.
Will BATCHman find the WinMobile intact?
Will Cmdlet finalize the solution for Handle?
Will Tapeworm ever get a speaking part?
Find out at the same BATCHtime, same BATCHchannel in the next episode of BATCHman and Cmdlet!
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy