此主题的所有先前帖子都涉及其用例的特定挑战。我认为让一篇帖子只处理从Python运行PowerShell脚本的最干净的方法并询问是否有人有比我发现的更好的解决方案是有用的。
似乎普遍接受的解决方案是绕过PowerShell试图在命令中解释不同的控制字符与使用文件时提供的Powershell命令的方式不同:
ps = 'powershell.exe -noprofile'
pscommand = 'Invoke-Command -ComputerName serverx -ScriptBlock {cmd.exe \
/c "dir /b C:\}'
psfile = open(pscmdfile.ps1, 'w')
psfile.write(pscommand)
psfile.close()
full_command_string = ps + ' pscmdfile.ps1'
process = subprocess.Popen(full_command_string , shell=True, \
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
当你的python代码每次调用它时都需要更改Powershell命令的参数时,你最终会编写并删除很多临时文件,以便subprocess.Popen运行。它工作得很好,但它不必要,也不是很干净。能够整理并希望得到我可以对我找到的解决方案所做的任何改进的建议真的很好。
不是将文件写入包含PS命令的磁盘,而是使用io模块创建虚拟文件。假设“日期”和“服务器”字符串作为包含此代码的循环或函数的一部分被输入,当然不包括导入:
import subprocess
import io
from string import Template
raw_shellcmd = 'powershell.exe -noprofile '
- 填充服务器和日期变量的循环开始 -
raw_pslistcmd = r'Invoke-Command -ComputerName $server -ScriptBlock ' \
r'{cmd.exe /c "dir /b C:\folder\$date"}'
pslistcmd_template = Template(raw_pslistcmd)
pslistcmd = pslistcmd_template.substitute(server=server, date=date)
virtualfilepslistcommand = io.BytesIO(pslistcmd)
shellcmd = raw_shellcmd + virtualfilepslistcommand.read()
process = subprocess.Popen(shellcmd, shell=True, stdout=subprocess.PIPE, \
stderr=subprocess.PIPE)
- 循环结束 -
答案 0 :(得分:2)
可以说最好的方法是使用powershell.exe -Command
而不是将PowerShell命令写入文件:
pscommand = 'Invoke-Command ...'
process = subprocess.Popen(['powershell.exe', '-NoProfile', '-Command', '"&{' + pscommand + '}"'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
确保pscommand
字符串中的双引号已正确转义。
请注意,shell=True
仅在某些边缘情况下是必需的,不应在您的方案中使用。来自documentation:
在
shell=True
的Windows上,COMSPEC
环境变量指定默认shell。您需要在Windows上指定shell=True
的唯一时间是您希望执行的命令内置于shell中(例如目录或复制)。您不需要shell=True
来运行批处理文件或基于控制台的可执行文件。
答案 1 :(得分:0)
为此花了很多时间。
我认为从python运行powershell命令对许多人来说可能没有意义,尤其是专门在Windows环境中工作的人。相对于powershell,python具有许多明显的优点,因此能够在python中执行所有业务逻辑,然后在远程服务器上有选择地执行powershell的能力确实很棒。
我现在已经完成了对“ winrmcntl”模块的一些改进,不幸的是,由于公司政策的原因,我无法共享该模块,但这是我对任何想做类似事情的人的建议。如果您直接在目标框中的PS中键入内容,则该模块应将未修改的PS命令或脚本块作为运行时的输入。一些技巧:
如果您不想在遍历python-> windows-> powershell-> powershell堆栈时“按摩” powershell命令,以使其在目标框上按预期工作,这是我最一致的方法我们发现,是将您的一个衬板和脚本块都写到一个ps_buffer.ps1文件中,然后将其输入到源代码框中的powershell中,以便每个process.popen看起来完全相同,但是ps_buffer.ps1的内容随每次执行而变化。 / p>
powershell.exe ps_buffer.ps1
为使python代码保持整洁,将json文件或类似文件中的powershell one衬里列表以及要运行的脚本块的指针保存到静态文件中非常好。您将json文件作为有序的dict加载,并根据您正在执行的命令循环执行。
“脚本块”和“服务器”是提供给该模块或功能的值
import subprocess
from string import Template
scriptblock = 'Get-ChildItem' #or a PS scriptblock as elaborate as you need
server = 'serverx'
psbufferfile = os.path.join(tempdir, 'pscmdbufferfile_{}.ps1'.format(server))
fullshellcmd = 'powershell.exe {}'.format(psbufferfile)
raw_pscommad = 'Invoke-Command -ComputerName $server -ScriptBlock {$scriptblock}'
pscmd_template = Template(raw_pscommand)
pscmd = pscmd_template.substitute(server=server, scriptblock=scriptblock)
try:
with open(psbufferfile, 'w') as psbf:
psbf.writelines(pscmd)
....
try:
process = subprocess.Popen(fullshellcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = process.communicate()
....