【问题标题】:What's the best way to execute PowerShell scripts from Python从 Python 执行 PowerShell 脚本的最佳方法是什么
【发布时间】:2018-04-16 02:14:24
【问题描述】:

之前关于该主题的所有帖子都针对其用例处理特定挑战。我认为有一个帖子只处理从 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 运行。它工作得很好,但它是不必要的而且不是很干净。能够整理并希望获得有关我可以对找到的解决方案进行的任何改进的建议真是太好了。

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

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)

--循环结束--

【问题讨论】:

    标签: python python-2.7 powershell


    【解决方案1】:

    可以说最好的方法是使用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 中时(例如 dircopy)。您不需要shell=True 来运行批处理文件或基于控制台的可执行文件。

    【讨论】:

    • 这行得通,我只需要使 pscommand 成为一个包含完整命令的字符串,以放入 subprocess.Popen。因为我得到了一个模糊的 PS 相关错误。 process = subprocess.Popen(pscommand, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    【解决方案2】:

    在这方面花了相当多的时间。

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

    我现在已经对我的“winrmcntl”模块进行了几项改进,很遗憾,由于公司政策,我无法分享这些改进,但这是我对任何想做类似事情的人的建议。如果您直接在目标框中的 PS 中键入,该模块应将未修改的 PS 命令或脚本块作为输入,因为您将运行它。一些技巧:

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

      powershell.exe ps_buffer.ps1

    • 1234563您将 json 文件加载为有序 dict 并根据您正在执行的操作循环发出命令。
    • 怎么强调都不为过,尽可能尝试使用最新的稳定版 PS,但更重要的是,客户端和服务器必须使用相同的版本。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-18
      • 2011-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多