{"id":12671,"date":"2011-09-17T00:01:00","date_gmt":"2011-09-17T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/09\/17\/batchman-writes-a-powershell-script-to-automate-handle\/"},"modified":"2011-09-17T00:01:00","modified_gmt":"2011-09-17T00:01:00","slug":"batchman-writes-a-powershell-script-to-automate-handle","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/batchman-writes-a-powershell-script-to-automate-handle\/","title":{"rendered":"BATCHman Writes a PowerShell Script to Automate Handle"},"content":{"rendered":"<p><strong>Summary<\/strong>: Windows PowerShell superhero BATCHman writes a script to automate the Sysinternals Handle tool.<\/p>\n<p>Microsoft Scripting Guy Ed Wilson here. Today, we continue the BATCHman series as the titular hero battles Tapeworm.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/i.technet.microsoft.com\/bb410849.Batchman_Icon_05_bluebatchman_082511(en-us,MSDN.10).png\"><img decoding=\"async\" style=\"max-width: 550px\" border=\"0\" alt=\"\" src=\"http:\/\/i.technet.microsoft.com\/bb410849.Batchman_Icon_05_bluebatchman_082511(en-us,MSDN.10).png\" \/><\/a>&nbsp;<\/p>\n<p>When the digital crash <br \/>In a blink and a splash <br \/>A gleam in the night <br \/>To make all wrongs right <br \/>Our heroes fly out <br \/>And there is no doubt <br \/>That evil will fall true <br \/>At the sight of the blue<\/p>\n<p>The one and only .NET Duo&mdash;BATCHman and Cmdlet!<\/p>\n<p>&nbsp;<\/p>\n<p>When last we saw our heroes, Tapeworm had taken control of the files in the Redmond Pocket Protector Recycling Plant, paralyzing the backup system. BATCHman and Cmdlet were working on a solution to automate Handle.exe. They were close to a solution as Cmdlet had discovered the output of Handle.exe was an object that could be manipulated in Windows PowerShell, an object that Select-String could work with.<\/p>\n<p>No sooner were they close to working on a solution than the alarm system was blaring on the WinMobile.<\/p>\n<p>Tearing down the stairs and bursting through the front door were BATCHman and Cmdlet. The sight that met their eyes was too much.<\/p>\n<p>The WinMobile was getting a parking ticket. Cmdlet looked up.<\/p>\n<p>&ldquo;I thought you were taking your 40-speed carbon fiber, midnight blue special to the crime scene. &lsquo;We&rsquo;re a Green company,&rsquo; you said.&rdquo;<\/p>\n<p>BATCHman coughed. &ldquo;Well, I did. From the top of the stairs to the WinMobile. I do after all have to arrive in style! I am&hellip;BATCHMAN!&rdquo;<\/p>\n<p>Cmdlet snickered. &ldquo;Style apparently didn&rsquo;t include the two dollars in quarters for the parking meter.&rdquo;<\/p>\n<p>BATCHman quickly dumped his change into the meter and collected his &ldquo;prize,&rdquo; a $140 ticket for an unpaid meter and for taking up two parking spots. &ldquo;Must look into running compression on the WinMobile,&rdquo; he mused.<\/p>\n<p>Running back up the stairs, they headed back to the task: automating Handle.exe.<\/p>\n<p>&ldquo;So, Cmdlet, let&rsquo;s see what we have now. With the following sequence in Windows PowerShell, we can grab the output from Handle.exe when searching for open DOCX files.&rdquo;<\/p>\n<p style=\"padding-left: 30px\">$ScreenOutput=.\\HANDLE.EXE DOCX<\/p>\n<p>&ldquo;And then with the following Select-String statements, identify the parts of the output that contain the text with <b>ProcessID<\/b> and <b>FileHandle<\/b>.&rdquo;<\/p>\n<p style=\"padding-left: 30px\">$ProcessIDResults=$ScreenOutput | SELECT-STRING &ndash;pattern &lsquo;pid: [\\w]*&rsquo;<br \/>$FileHandleResults=$ScreenOutput | SELECT-STRING &ndash;pattern &lsquo;File [s\\S]*?:&rsquo;<\/p>\n<p>&ldquo;With this output within <b>Select-String<\/b>, we found there was also an index and length of the output to work with for each match.&rdquo;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4532.hsg-9-17-11-1.jpg\"><img decoding=\"async\" border=\"0\" alt=\"\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4532.hsg-9-17-11-1.jpg\" width=\"408\" height=\"190\" \/><\/a><\/p>\n<p>Cmdlet thought. &ldquo;So what we need now is to find a way to pull the information out of these matches. So maybe we should look at what data we now have. We know where <b>pid:<\/b> and <b>type: File<\/b> exist because of the properties in <b>Matches<\/b>.<\/p>\n<p>BATCHman could see the little hamster running in the wheel in Cmdlet&rsquo;s brain. &ldquo;Continue&hellip;&rdquo;<\/p>\n<p>&ldquo;So we can use <b>substring()<\/b> and pull out based upon the index and length of that content&rsquo;s position in the string.&rdquo; Cmdlet quickly typed a line in Windows PowerShell.<\/p>\n<p style=\"padding-left: 30px\">$ProcessIDResults[0].tostring().substring(19,9)<\/p>\n<p>They looked at the screen as a result appeared.<\/p>\n<p style=\"padding-left: 30px\">pid: 8520<\/p>\n<p>&ldquo;Cmdlet, excellent job. Now we need to this via the properties instead, and for both values.&rdquo; BATCHman quickly took over.<\/p>\n<p style=\"padding-left: 30px\">$ProcessIDIndex=$ProcessIDResults[0].matches[0].Index<br \/>$ProcessIDLength=$ProcessIDResults[0].matches[0].Length<\/p>\n<p style=\"padding-left: 30px\">$ProcessIDResults[0].tostring().substring($ProcessIDIndex,$ProcessIDLength)<\/p>\n<p style=\"padding-left: 30px\">$FileHandleIndex=$FileHandleResults[0].matches[0].Index<br \/>$FileHandleLength=$FileHandleResults[0].matches[0].Length<\/p>\n<p style=\"padding-left: 30px\">$FileHandleResults[0].tostring().substring($FileHandleIndex,$FileHandleLength)<\/p>\n<p>BATCHman looked at the output on the screen.<\/p>\n<p style=\"padding-left: 30px\">pid: 8520<\/p>\n<p style=\"padding-left: 30px\">File 164:<\/p>\n<p>&ldquo;Now we need to clean this up,&rdquo; mumbled BATCHman as he scratched his chin. &ldquo;We&rsquo;ll need to skip the first four characters of the <b>pid:<\/b> by adjusting the starting point in the substring and the length appropriately. We will skip <b>File<\/b> because it&rsquo;s only four characters long. But for the <b>FileHandle<\/b>, we&rsquo;ll drop an extra character to lose that colon on the end.&rdquo;<\/p>\n<p style=\"padding-left: 30px\">$ProcessIDResults[0].tostring().substring($ProcessIDIndex+4,$ProcessIDLength-4)<br \/>$FileHandleResults[0].tostring().substring($FileHandleIndex+4,$FileHandleLength-5)<\/p>\n<p style=\"padding-left: 30px\">8520<\/p>\n<p style=\"padding-left: 30px\">164<\/p>\n<p>&ldquo;Now we&rsquo;ve just got to store it as a variable and tack on a <b>trim()<\/b> method to remove any extraneous spaces leading or trailing.&rdquo;<\/p>\n<p style=\"padding-left: 30px\">$ProcessID=$ProcessIDResults[0].tostring().substring($ProcessIDIndex+4,$ProcessIDLength-4).trim()<br \/>$FileHandle=$FileHandleResults[0].tostring().substring($FileHandleIndex+4,$FileHandleLength-5).trim()<\/p>\n<p>Cmdlet looked and realized that and this point they could automate the application. &ldquo;So, BATCHman, we could just simply at this point step through the results with a <b>Foreach<\/b> statement and call up Handle.exe!&rdquo;<\/p>\n<p>BATCHman quickly typed a line to verify this would work on the DOCX files.<\/p>\n<p style=\"padding-left: 30px\">$TotalResults=$ProcessIdResults.count<\/p>\n<p style=\"padding-left: 30px\">For ($Counter=0; $Counter &ndash;lt $TotalResults; $Counter++)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">$ProcessIDIndex=$ProcessIDResults[$Counter].matches[0].Index<br \/>$ProcessIDLength=$ProcessIDResults[$Counter].matches[0].Length<\/p>\n<p style=\"padding-left: 30px\">$FileHandleIndex=$FileHandleResults[$Counter].matches[0].Index<br \/>$FileHandleLength=$FileHandleResults[$Counter].matches[0].Length<\/p>\n<p style=\"padding-left: 30px\">$ProcessID=$ProcessIDResults[$Counter].tostring().substring($ProcessIDIndex+4,$ProcessIDLength-4).trim()<br \/>$FileHandle=$FileHandleResults[$Counter].tostring().substring($FileHandleIndex+4,$FileHandleLength-5).trim()<\/p>\n<p style=\"padding-left: 30px\">(&amp; &#8216;.\\Handle Program\\handle.exe&#8217; -c $FileHandle -p $ProcessID -y)<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p>Smiling, BATCHman noted as files closed on the screen. He pressed a Ctrl+C to stop the process.<\/p>\n<p>&ldquo;Now, Cmdlet, all we need to do to get this automated and running is turn this into a function so that Jane can run her backups properly and these old pocket protectors can get back to being recycled.&rdquo;<\/p>\n<p>BATCHman quickly rewrote it into a single Windows PowerShell script with a function to specify types of files to close that Jane could run automatically on the file server.<\/p>\n<p style=\"padding-left: 30px\">function global:close-file ($name) {<br \/>$ScreenOutput=(&amp; &#8216;.\\Handle\\handle.exe&#8217; $name)<br \/>$ProcessIDResults=$ScreenOutput | SELECT-STRING &ndash;pattern &lsquo;pid: [\\w]*&rsquo;<br \/>$FileHandleResults=$ScreenOutput | SELECT-STRING &ndash;pattern &lsquo;File [s\\S]*?:&rsquo;<\/p>\n<p style=\"padding-left: 30px\">$TotalResults=$ProcessIdResults.count<\/p>\n<p style=\"padding-left: 30px\">For ($Counter=0; $Counter &ndash;lt $TotalResults; $Counter++)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">$ProcessIDIndex=$ProcessIDResults[$Counter].matches[0].Index<br \/>$ProcessIDLength=$ProcessIDResults[$Counter].matches[0].Length<\/p>\n<p style=\"padding-left: 30px\">$FileHandleIndex=$FileHandleResults[$Counter].matches[0].Index<br \/>$FileHandleLength=$FileHandleResults[$Counter].matches[0].Length<\/p>\n<p style=\"padding-left: 30px\">$ProcessID=$ProcessIDResults[$Counter].tostring().substring($ProcessIDIndex+4,$ProcessIDLength-4).trim()<br \/>$FileHandle=$FileHandleResults[$Counter].tostring().substring($FileHandleIndex+4,$FileHandleLength-5).trim()<\/p>\n<p style=\"padding-left: 30px\">(&amp; &#8216;.\\Handle Program\\handle.exe&#8217; -c $FileHandle -p $ProcessID -y)<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">CLOSE-HANDLE DOCX<br \/>CLOSE-HANDLE XLSX<br \/>CLOSE-HANDLE ACCDB<br \/>CLOSE-HANDLE PPTX<\/p>\n<p>BATCHman handed the script to Jane and scheduled it to run every 10 minutes to thwart Tapeworm&rsquo;s efforts. In moments, there was a shriek from the basement! &ldquo;AIAGHIAGHI!!!! Curse you! Curse you!&rdquo;<\/p>\n<p>&ldquo;Jane, quickly, let&rsquo;s trigger that backup now!&rdquo; commanded BATCHman.<\/p>\n<p>While the backup ran, BATCHman and Cmdlet followed the shrieking sounds of Tapeworm to his hideaway, a forgotten IT storage room nicknamed the Pit of Eternal Sorrow.<\/p>\n<p>&ldquo;A-ha! We have you now Tapeworm! What have you got to say for yourself?&rdquo;<\/p>\n<p>&ldquo;Bah! Curses! I would have gotten away with it if hadn&rsquo;t been for you meddling kids and Windows PowerShell! BAAAAAHAHH!!!&rdquo; he cursed as BATCHman hauled him off. Scooby Doo was nowhere to be found.<b><i><\/i><\/b><\/p>\n<p>&nbsp;<\/p>\n<p>I want to thank Sean for another exciting episode of BATCHman. Join us tomorrow for the exciting conclusion to the <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/batchman\/\">BATCHman series<\/a>.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Windows PowerShell superhero BATCHman writes a script to automate the Sysinternals Handle tool. Microsoft Scripting Guy Ed Wilson here. Today, we continue the BATCHman series as the titular hero battles Tapeworm. &nbsp; &nbsp; When the digital crash In a blink and a splash A gleam in the night To make all wrongs right Our [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[284,56,3,154,45],"class_list":["post-12671","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-batchman","tag-guest-blogger","tag-scripting-guy","tag-sean-kearney","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Windows PowerShell superhero BATCHman writes a script to automate the Sysinternals Handle tool. Microsoft Scripting Guy Ed Wilson here. Today, we continue the BATCHman series as the titular hero battles Tapeworm. &nbsp; &nbsp; When the digital crash In a blink and a splash A gleam in the night To make all wrongs right Our [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/12671","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=12671"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/12671\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=12671"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=12671"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=12671"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}