1

I have to invoke two powershell commands via a python script. I am currently using subprocess.Popen for this.

The first command returns a powershell object. Example:

PS>$temp = <powershell_command_1>

The $temp object has multiple properties in it. The entire object has to be passed to a second powershell object in the same script. eg.

PS><powershell_command_2> -object $temp

If $temp were to be a string, I have no problems with passing the python string variable. However, in this case $temp is an object. How can I pass this value to the second PS cmdlet?

Here is the traceback

Traceback (most recent call last):
  File "file.py", line 430, in <module>
    status = c1.configure_cluster()
  File "file.py", line 319, in configure_cluster
    configure_cluster = subprocess.Popen([self.powershell_binary,'-ExecutionPolicy','ByPass',r'Cmdlet2', temp], stdout=subprocess.PIPE)
  File "C:\Python34\lib\subprocess.py", line 858, in __init__
    restore_signals, start_new_session)
  File "C:\Python34\lib\subprocess.py", line 1085, in _execute_child
    args = list2cmdline(args)
  File "C:\Python34\lib\subprocess.py", line 663, in list2cmdline
    needquote = (" " in arg) or ("\t" in arg) or not arg
TypeError: Type str doesn't support the buffer API
Ajay K
  • 354
  • 1
  • 4
  • 14
  • Have you looked at `Invoke-Command` cmdlet? – arco444 Nov 18 '14 at 16:23
  • Do you have to run the two powershell commands separately or can they be run at the same time? e.g. `Powershell -command { command_1 | command_2 }` if the second command accepts a pipeline, or `Powershell -command { command_2 -object (command_1) }` if it doesn't. – Duncan Nov 18 '14 at 16:30
  • They have to be run separately. The python script calls cmdlet 1, then does other processing based on the value of the output variable and subsequently has to call cmdlet 2, with the value of $temp. – Ajay K Nov 18 '14 at 16:51
  • Two things come to mind: use `Export-CliXml`/`Import-CliXml` to persist the objects; or Powershell remote sessions are supposed to be persistent so you may be able to create a session, keep it running, and use Invoke-Command to send it further commands. – Duncan Nov 18 '14 at 19:23

1 Answers1

2

If the two commands can be run together then just run them together, but as you say you have to run them separately you could try using Export-Clixml to save the object to a file then Import-Clixml to reload it. Here's a working example:

#!/usr/bin/env python3
import base64, subprocess, sys

def powershell(cmd, input=None):
    cmd64 = base64.encodebytes(cmd.encode('utf-16-le')).decode('ascii').strip()
    stdin = None if input is None else subprocess.PIPE
    process = subprocess.Popen(["powershell.exe", "-NonInteractive", "-EncodedCommand", cmd64], stdin=stdin, stdout=subprocess.PIPE)
    if input is not None:
        input = input.encode(sys.stdout.encoding)
    output, stderr = process.communicate(input)
    output = output.decode(sys.stdout.encoding).replace('\r\n', '\n')
    return output

command=r"""
$data = Get-ChildItem C:\Python34
$data | Export-Clixml -Path c:\temp\state.xml
$data
"""
output = powershell(command)
print(output)
print("Do some processing here...")
result = """NEWS.txt=1
README.txt=1
\u00a3.txt=1
"""
command = r"""
$in = $input|Out-String
$selected = $in | ConvertFrom-StringData
$data = Import-Clixml -Path c:\\temp\\state.xml
$data |? { $selected.ContainsKey($_.Name) } | Select Length,FullName
"""
output = powershell(command, result)
print(output)

Which gives the following output:

C:\Temp>py state.py


    Directory: C:\Python34


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        24/10/2014     11:52            DLLs
d----        24/10/2014     11:52            Doc
d----        24/10/2014     11:52            include
d----        24/10/2014     11:52            Lib
d----        24/10/2014     11:52            libs
d----        05/11/2014     16:53            Scripts
d----        24/10/2014     11:52            tcl
d----        24/10/2014     11:52            Tools
-a---        06/10/2014     22:19      31073 LICENSE.txt
-a---        21/09/2014     21:03     367504 NEWS.txt
-a---        06/10/2014     22:15      27136 python.exe
-a---        06/10/2014     22:15      27648 pythonw.exe
-a---        06/10/2014     22:10       6942 README.txt
-a---        19/11/2014     11:59          5 £.txt



Do some processing here...

                                 Length FullName
                                 ------ --------
                                 367504 C:\Python34\NEWS.txt
                                   6942 C:\Python34\README.txt
                                      5 C:\Python34\£.txt

You could of course load the XML into Python and manipulate it, in which case use ConvertTo-Xml -as string rather than Export-CliXml though I'm not quite sure how you get that converted back to Powershell objects rather than an xml object. (Edit: Powershell's import-clixml from string covers round-tripping objects through Clixml without using a file).

Community
  • 1
  • 1
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • Thanks, I adopted a portion of your solution, to solve my problem. I used the:command = r""" .... """ and rearranged some of my code, to get rid of the intermediate processing. – Ajay K Nov 27 '14 at 16:45