Thursday, February 21, 2008

Embedding PowerShell in IronPython and vice versa -- “two great tastes that taste great together”

PowerShell and Python may both be scripting languages, but their strengths are in different areas. One place where PowerShell excels is in getting prettily formatted text output out of your windows system calls, and making a lot of standard queries without having to go to all the hassle that diving into the System.Management namespace often involves (writing your own WQL queries and the like).

So, why not embed the one script inside the other?

import clr
import System.Collections
clr.AddReference("System.Management.Automation")
from System.Management.Automation import *
from System.Management.Automation.Runspaces import *
import System.IO
def RunScript(script):
runspace = RunspaceFactory.CreateRunspace()
runspace.Open()
pipeline = runspace.CreatePipeline()
cache = System.IO.Path.GetTempFileName()
pipeline.Commands.AddScript(script)
## flatten at the PowerShell level from collection
## of PSObjects to the PowerShell displayed form
## if that is all that is required.
pipeline.Commands.Add("Out-String")
results = pipeline.Invoke()
pickled = []
for thing in results:
pickled.append(thing.ToString())
return pickled
if __name__ == '__main__':
pickled = RunScript("Get-Process")
for thing in pickled:
print thing
view raw gistfile1.py hosted with ❤ by GitHub

In the example, we get a single pretty-printed list of all the processes. If we hadn't added the Out-String, we would have a collection of System.Diagnostics.Process objects that we could work with as .Net objects in their own right. And objects can be passed in via runspace.SessionStateProxy.SetVariable("variableName", theObject) and picked up as $variableName in the PowerShell script.

As IronPython can be embedded in .Net, it can of course be embedded in PowerShell, like this (for 1.x)

[reflection.assembly]::LoadFrom("C:\IronPython-1.1\IronPython.dll")
$pythonEngine = New-Object -TypeName "IronPython.Hosting.PythonEngine"
$pythonEngine.Execute("print 'Hello World!'")
view raw gistfile1.ps1 hosted with ❤ by GitHub

The 2.0 code is similar in spirit, but different in detail:

[reflection.assembly]::LoadFrom("C:\IronPython-2.0A7\IronPython.dll")
$pythonEngine = [ironpython.hosting.pythonengine]::CurrentEngine
$pythonEngine.Execute("print 'Hello World!'")
view raw gistfile1.ps1 hosted with ❤ by GitHub

H/t CodeProject

1 comment :

David Seruyange said...

I'd experimented with something like this (check http://metadeveloper.blogspot.com/2007/11/web-shell-prototype-ironpython.html) and had some limited success. I'll see if I can get it to work with the approach you are using. I had a hard time keeping a "context" with the shell versus just a command at a time.