Powershell Powers, Activate!
Yesterday, I had a problem to solve. I didn’t want to stay at work until 9pm, waiting for another team’s process to publish a file. (Grossly simplified, but you get the idea). I also didn’t want to log in from home at 9pm in order to wait for it to happen (I did that the night before).
So I used Powershell! Could I have used a batch file? Of course! However, I know with Powershell I can just chain commands together via a semi-colon.
I searched for “Powershell sleep”, and saw immediately that there was, in fact, a Powershell sleep command: start-sleep.
My final commandline:
start-sleep -s 7000 ; GetFileCommand ; msbuild /t:clean,build
And I could just walk away, knowing it would be waiting for me when I got into today.
(That didn’t happen, but it was unrelated to the Powershell issue.)
However, that wasn’t my only use of Powershell yesterday. The MSBuild Project system defines a build through a series of linked .XML files. I uncovered an issue where a particular task wasn’t being completed as I expected. I could, through the wonders of “Find”, locate where I EXPECTED the work to be taking place, but in a 10,000+ line XML file, scrolling upward to find the parent is not entirely pleasant.
So I used code.
I read the XML into an XML object, then found the tag I was looking for. I then got an XML Navigator object for where I was in the document, and walked back up the tree until I found something identifiable (it turned out I was screwed). All told, it took me less time to puzzle out (via get-member) how to do so under Powershell than it would have taken for me to write a real program, or to find it by hand.
Here’s the entirety of what I wrote:
$xmldoc = [xml] [string]::join(“`n”, (gc -read 10kb Native.Build.targets))
$xmldoc | get-member
$xmldoc.GetElementsByTagName(‘Internal_LinkOutputFile’)
$xmldoc.GetElementsByTagName(‘Internal_LinkOutputFile’) | get-member
$xmldoc.GetElementsByTagName(‘Internal_LinkOutputFile’).Item(0)
$xmldoc.GetElementsByTagName(‘Internal_LinkOutputFile’).Item(0) | get-member
$nav = $xmldoc.GetElementsByTagName(‘Internal_LinkOutputFile’).Item(0).CreateNavigator()
$nav
$nav | get-member
$nav.MoveToParent()
$nav
$nav.MoveToParent()
$nav
There was an awful lot of get-member calls, but I didn’t need to know ANYTHING else.
Yay Powershell!
PowerShell, huh? That sounds almost as nifty as basic Unix!
Ha. Ha. Ha.
I can do pretty much anything possible via bash or csh via Powershell — however, I’m being extremely lazy about it. Conceivably (I’m not testing it, and I’m not a Powershell expert), I should be able to:
([xml] [string]::join(“`n”, (gc -read 10kb Native.Build.targets))).GetElementsByTagName(“Internal_LinkOutputFile”) | % { $temp = $_.CreateNavigator() ; while ($temp.MoveToParent()) { } ; $temp }
Where EVERYTHING in the pipeline is an object, rather than restricted to just text flow.
You can go back to your pipe characters and output redirection, I’ll stick with my strongly-typed scripting interactive command intepreter. =]
Sounds like you should check out the new Select-XML cmdlet in CTP3:
Select-Xml
(0)-XPath
(1)-Xml | -Node (ByValue)
[-Namespace ]
Select-Xml
(0)-XPath
-Content (ByValue)
[-Namespace ]
Select-Xml
(0)-XPath
(1)-Path | -PSPath (ByName)
[-Namespace ]
Here is an example of it working:
[5308:0]PS> dir . *xml -Recurse |select-xml ‘//SelectionSet[Name=”CertificateProviderTypes”]’ |fl
Node : SelectionSet
Path : C:\Windows\System32\WindowsPowerShell\v1.0\Certificate.Format.ps1xml
Pattern : //SelectionSet[Name=”CertificateProviderTypes”]
Experiment! Enjoy! Engage!
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Thanks, Jeffrey! That looks promising — although I hope I don’t have to pull apart a build system XML the next time I need to walk a document tree. =]