从 Python 执行 PowerShell 脚本的最佳方式是什么 [英] What's the best way to execute PowerShell scripts from Python

查看:33
本文介绍了从 Python 执行 PowerShell 脚本的最佳方式是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

之前关于此主题的所有帖子都针对其用例处理了特定挑战.我认为有一个帖子只讨论从 Python 运行 PowerShell 脚本的最干净的方法,并询问是否有人有比我发现的更好的解决方案.

All the previous posts on this topic deal with specific challenges for their use case. I thought it would be useful to have a post only dealing with the cleanest way to run PowerShell scripts from Python and ask if anyone has an better solution than what I found.

解决 PowerShell 试图以不同的方式解释命令中的不同控制字符的普遍接受的解决方案似乎是在使用文件时提供 Powershell 命令的目的:

What seems to be the generally accepted solution to get around PowerShell trying to interpret different control characters in your command differently to what's intended is to feed your Powershell command in using a file:

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 运行.它工作得很好,但它是不必要的,也不是很干净.能够整理并希望获得有关我可以对找到的解决方案进行的任何改进的建议真是太好了.

When your python code needs to change the parameters for the Powershell command each time you invoke it you end up writing and deleting a lot of temporary files for subprocess.Popen to run. It works perfectly but it's unnecessary and not very clean. It's really nice to be able to tidy up and wanted to get suggestions on any improvements I could make to the solution I found.

不是将包含 PS 命令的文件写入磁盘,而是使用 io 模块创建虚拟文件.假设日期"和服务器"字符串作为包含此代码的循环或函数的一部分输入,当然不包括导入:

Instead of writing a file to disk containing the PS command create a virtual file using the io module. Assuming that the "date" and "server" strings are being fed in as part of a loop or function that contains this code, not including the imports of course:

import subprocess
import io
from string import Template
raw_shellcmd = 'powershell.exe -noprofile '

--填充服务器和日期变量的循环开始--

--start of loop with server and date variables populated--

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)

--循环结束--

推荐答案

在这个问题上花费了相当多的时间之后.

After spending a fair amount of time on this.

我认为从 python 运行 powershell 命令对很多人来说可能没有意义,尤其是专门在 windows 环境中工作的人.然而,与 powershell 相比,python 有许多明显的优势,因此能够在 python 中完成所有业务逻辑,然后有选择地在远程服务器上执行 powershell 确实是一件很棒的事情.

I think that running powershell commands from python may not make sense to a lot of people, especially people who work exclusively in windows environments. There are numerous clear advantages to python over powershell however so the ability to do all your business logic in python and then selectively execute powershell on remote servers is truly a great thing.

我现在已经对我的winrmcntl"模块进行了几次改进,不幸的是,由于公司政策,我无法分享这些改进,但这是我对任何想做类似事情的人的建议.该模块应将未修改的 PS 命令或脚本块作为输入,就像您在目标框中直接输入 PS 时运行它一样.一些技巧:

I've now been through several improvements of my "winrmcntl" module which I can't share due to company policy unfortunately but here is my advice to anyone who would like to do something similar. The module should take as input an unmodified PS command or scriptblock as you'd run it if you were typing directly in PS on the destination box. A few tricks:

  • 为避免权限问题,请确保运行您的 python 脚本的用户,因此通过 process.Popen 运行 powershell.exe 的用户是在您调用命令指向的 Windows 框上具有正确权限的用户.我们使用一个企业调度程序,它有 Windows vms 作为代理,python 代码在这些代理上运行.
  • 您有时很少但仍然会从 powershell 领域获得奇怪的深奥异常,如果它们特别像我看到的那个奇怪的时间,微软会挠挠头,让您做耗时的应用程序堆栈追踪.这不仅耗时,而且很难正确处理,因为它是资源密集型的,您不知道下一次异常何时发生.在我看来,如果某个文本出现在这些异常中,那么解析异常的输出并重试最多 x 次会更好也更容易.我在 winrmcntl 模块中保留了一个字符串列表,该模块当前包含一个字符串.
  • 如果您不想在 powershell 命令遍历 python -> windows -> powershell -> powershell 堆栈时按摩"它们以使它们在目标框上按预期工作,这是我最一致的方法我发现是将你的一个衬垫和脚本块写入一个 ps_buffer.ps1 文件,然后你将它提供给源框上的 powershell,以便每个 process.popen 看起来完全相同,但 ps_buffer.ps1 的内容随着每次执行而变化.

  • To avoid permission difficulties, ensure the user running your python script and hence the one running powershell.exe via process.Popen is the user that has the correct permissions on the windows box you're invoke-command is pointing at. We use an enterprise scheduler which has windows vms as agents on which the python code lives which takes care of that.
  • You will sometimes rarely but still get the odd esoteric exception from powershell land, if they're anything like the one in particular I saw the odd time, microsoft scratch their heads at a little and get you to do time consuming application stack tracing. This is not only time consuming but very difficult to get right because it's resource intensive and you don't know when the exception will next occur. In my opinion, it's much better and easier to parse the output of the exception and retry up to x number of times if a certain text appears in those exceptions. I keep a list of strings in my winrmcntl module which currently contains a single string.
  • If you want to not have to "massage" the powershell commands as they traverse the python -> windows -> powershell -> powershell stack to make them work as expected on destination boxes, the most consistent method I've found is to write your one liners and scriptblocks alike into a ps_buffer.ps1 file which you then feed to powershell on the source box so that every process.popen looks exactly the same but the content of ps_buffer.ps1 changes with each execution.

powershell.exe ps_buffer.ps1

为了让您的 Python 代码保持干净整洁,最好将 json 文件或类似文件中的 PowerShell 单行列表以及指向要运行的脚本块的指针保存到静态文件中.您将 json 文件加载为一个有序的 dict 并根据您正在执行的操作循环发出命令.

To keep your python code nice and clean, it's great having your list of powershell one liners in a json file or similar as well as pointers to scriptblocks you want to run saved into static files. You load up your json file as an ordered dict and cycle through issuing commands based on what you're doing.

"scriptblock" 和 "server" 是提供给这个模块或函数的值

"scriptblock" and "server" are the values fed to this module or function

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()
....        

这篇关于从 Python 执行 PowerShell 脚本的最佳方式是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆