使用Python subprocess调用PowerShell Invoke-Command报错问题
我想用Python的subprocess模块在PowerShell中创建New-PSSession远程会话,再通过该会话执行Get-Process命令,但运行代码时报错。
我的代码:
import subprocess result=subprocess.run(['powershell.exe',' New-PSSession -ComputerName 12.455.66.7777 -Credential (New-Object System.Management.Automation.PSCredential("username",(ConvertTo-SecureString "password" -AsPlainText -Force)))'],capture_output=True) session=result.stdout.decode().strip() print(session) result=subprocess.run(["powershell.exe","-Command", f"Invoke-Command -Session {session} -ScriptBlock {{Get-Process}}"], capture_output=True) print(result)
错误信息:
CompletedProcess(args=['powershell.exe', '-Command', 'Invoke-Command -Session Id Name ComputerName ComputerType State ConfigurationName Availability\r\n -- ---- ------------ ------------ ----- ----------------- ------------\r\n 1 WinRM1 11.888.32.09 RemoteMachine Opened Microsoft.PowerShell Available -ScriptBlock {Get-Process}'], returncode=1, stdout=b'', stderr=b"At line:3 char:5\r\n+ 1 WinRM1 10.229.78.59 RemoteMachine Opened Mic ...\r\n+ ~~~~~~\r\n表达式或语句中出现意外的标记“WinRM1”。\r\nAt line:2 char:5\r\n+ -- ---- ------------ ------------ ----- --- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:7\r\n+ -- ---- ------------ ------------ ----- --- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:21\r\n+ ... ------------ ------------ ----- ----------- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:23\r\n+ ... ------------ ------------ ----- ------------- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:25\r\n+ ... ------------ ------------ ----- --------------- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:27\r\n+ ... ------------ ------------ ----- ----------------- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:29\r\n+ ... ------------ ------------ ----- ----------------- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:31\r\n+ ... ------------ ------------ ----- ----------------- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\nAt line:2 char:37\r\n+ ... -------- ------------ ----- ----------------- ----- ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n“--”运算符仅适用于变量或属性。\r\n未报告所有分析错误。请纠正报告的错误,然后重试。\r\n + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException\r\n + FullyQualifiedErrorId : UnexpectedToken\r\n \r\n")
问题原因
你犯了一个关键错误:New-PSSession的输出是格式化的表格文本(包含表头、分隔线和会话详情),而不是PowerShell可以直接识别的会话对象。当你把整个表格内容拼到Invoke-Command的-Session参数中时,PowerShell无法解析这堆文本为有效的会话对象,直接抛出语法错误。
解决方案
方法一:在同一个PowerShell会话中完成所有操作(推荐)
跨subprocess调用会丢失PowerShell的变量上下文,最好把创建会话、执行命令、关闭会话的逻辑放在同一个PowerShell命令里执行,这样可以直接使用会话变量,避免传递文本的问题。
修改后的代码:
import subprocess # 将所有PowerShell逻辑整合为一个命令块 ps_script = ''' # 创建凭据对象 $cred = New-Object System.Management.Automation.PSCredential( "username", (ConvertTo-SecureString "password" -AsPlainText -Force) ) # 创建远程会话 $session = New-PSSession -ComputerName 12.455.66.7777 -Credential $cred # 执行远程命令 Invoke-Command -Session $session -ScriptBlock { Get-Process } # 关闭会话(避免资源泄漏) Remove-PSSession -Session $session ''' # 执行PowerShell命令,指定text=True直接获取字符串输出 result = subprocess.run( ['powershell.exe', '-Command', ps_script], capture_output=True, text=True ) # 输出结果 print("命令执行结果:") print(result.stdout) if result.stderr: print("错误信息:") print(result.stderr)
方法二:提取会话ID进行调用(不推荐,仅作参考)
如果一定要分两次subprocess调用,需要从New-PSSession的输出中提取会话ID,再通过ID获取会话对象。可以用PowerShell的Select-Object只输出ID值:
修改后的代码:
import subprocess import re # 第一步:创建会话并提取ID create_session_cmd = ''' $cred = New-Object System.Management.Automation.PSCredential( "username", (ConvertTo-SecureString "password" -AsPlainText -Force) ) # 仅输出会话ID New-PSSession -ComputerName 12.455.66.7777 -Credential $cred | Select-Object -ExpandProperty Id ''' result = subprocess.run( ['powershell.exe', '-Command', create_session_cmd], capture_output=True, text=True ) session_id = result.stdout.strip() if not session_id: print("创建会话失败:", result.stderr) else: # 第二步:用会话ID执行远程命令 run_cmd = f''' Invoke-Command -Session (Get-PSSession -Id {session_id}) -ScriptBlock {{ Get-Process }} # 执行完关闭会话 Remove-PSSession -Id {session_id} ''' result = subprocess.run( ['powershell.exe', '-Command', run_cmd], capture_output=True, text=True ) print("命令输出:") print(result.stdout) if result.stderr: print("错误信息:") print(result.stderr)
注意事项
- 硬编码用户名和密码存在安全风险,建议使用环境变量或Windows凭据管理器来存储敏感信息
- 确保目标远程机器已经开启并配置了WinRM服务,且你的本地机器有权限连接
- 方法一的可靠性更高,因为会话对象在同一个PowerShell进程中维护,不会因为文本解析错误导致失败
内容的提问来源于stack exchange,提问作者lvsilver

