【问题标题】:Why is this subprocess.check_output line crashing my script?为什么这个 subprocess.check_output 行会使我的脚本崩溃?
【发布时间】:2019-01-13 00:13:26
【问题描述】:

我有一个脚本在 .pyw 中可以正常工作,但是当转换为 .exe 时,(编辑:实际上,当我使用带有参数 -w--windowed--noconsole 的 pyinstaller 时,它不会t 工作,但没有它们它工作)我发现这一行似乎使程序崩溃:

firstplan = subprocess.check_output(["powercfg", "-list"], shell=True ).split('\n')[3]

有人知道为什么吗?如果我将其注释掉,程序不会崩溃。我还有另外两条非常相似的线。

编辑:

也许把脚本放在这里是个好主意……

from __future__ import print_function
import os
# os.system('cls')
import psutil
import subprocess

loop = 1

while loop == (1):

    CPUload = (psutil.cpu_percent(interval=4))      # CPU load
    RAMload = (psutil.virtual_memory().percent)     # RAM load

    # os.system('cls')

    print("CPU Load: ", end="")         # Display CPU Load:
    print(CPUload, "%")                 # Display CPUload
    print("RAM Load: ", end="")         # Display CPU Load:
    print(str(RAMload) + " %")          # Display RAMload

    firstplan = subprocess.check_output(["powercfg", "-list"], shell=True ).split('\n')[3]      # Selects a line
    secondplan = subprocess.check_output(["powercfg", "-list"], shell=True ).split('\n')[4]
    thirdplan = subprocess.check_output(["powercfg", "-list"], shell=True ).split('\n')[5]

    firstplanID = ((firstplan.split(": "))[1].split("  (")[0])      # Extract XplanID from Xplan
    secondplanID = ((secondplan.split(": "))[1].split("  (")[0])
    thirdplanID = ((thirdplan.split(": "))[1].split("  (")[0])

    activeplan = subprocess.check_output(["powercfg", "/getactivescheme"])      # Find the currently active plan
    activeplanNAME = ((activeplan.split("("))[1].split(")")[0])     # Extract activeplanNAME from activeplan

    firstplanNAME = ((firstplan.split("("))[1].split(")")[0])       # Extract XplanNAME from Xplan
    secondplanNAME = ((secondplan.split("("))[1].split(")")[0])
    thirdplanNAME = ((thirdplan.split("("))[1].split(")")[0])


    if "High performance" in firstplanNAME:         # Identify which plan is High performance
        HighPerformance = firstplanNAME
        HighPerformanceID = firstplanID

    if "High performance" in secondplanNAME:
        HighPerformance = secondplanNAME
        HighPerformanceID = secondplanID

    if "High performance" in thirdplanNAME:
        HighPerformance = thirdplanNAME
        HighPerformanceID = thirdplanID

    if "Power saver" in firstplanNAME:              # Identify which plan is Power saver
        PowerSaver = firstplanNAME
        PowerSaverID = firstplanID

    if "Power saver" in secondplanNAME:
        PowerSaver = secondplanNAME
        PowerSaverID = secondplanID

    if "Power saver" in thirdplanNAME:
        PowerSaver = thirdplanNAME  
        PowerSaverID = thirdplanID


    if activeplanNAME == PowerSaver:            # Checks current plan name
        print("Active plan: Power saver")
    else:
        if activeplanNAME == HighPerformance:
            print("Active plan: High Performance")
        else: 
            subprocess.check_output(["powercfg", "/s", HighPerformanceID])          


    if CPUload < 44:    
        if RAMload > 90:
            if activeplanNAME == PowerSaver:
                subprocess.check_output(["powercfg", "/s", HighPerformanceID])
                print("Switching to High Performance by RAM load...")       

    if CPUload < 44:    
        if RAMload < 90:
            if activeplanNAME == HighPerformance:
                subprocess.check_output(["powercfg", "/s", PowerSaverID])
                print("Switching to Power saver...")                    

    if CPUload > 55:
        if activeplanNAME == PowerSaver:
            subprocess.check_output(["powercfg", "/s", HighPerformanceID])
            print("Switching to High Performance...")

有问题的行是第 21-23 行。

如需更多信息,请向下滚动至 cmets 和答案。

【问题讨论】:

  • 使用shell=True 将命令作为列表传递显然恰好在某些平台上工作,但实际上不是正确的事情。取出shell=True。 (不过,我认为这不会解决您的问题。)
  • 您共享的脚本中的缩进显然是错误的(靠近开头的while 循环之后的部分或全部行应该缩进)。请注意发布有效代码;缩进对 Python 尤其重要。另请参阅Markdown help
  • subprocess 文档有详细信息。如果你使用shell=True,你应该传入一个字符串供shell解析;使用shell=False(或没有shell=True),您需要传入已解析令牌的列表。
  • 显式比隐式好,所以改成shell=False虽然技术上把它去掉也会做同样的事情。
  • 调用一个 shell 三次来从同一个命令得到三行输出也是相当浪费的。只需执行pcfg = subprocess.check_output(['powercfg', '-list']).split('\n') 然后first = pcfg[3]second = pcfg[4] 等。

标签: python split subprocess pyinstaller strsplit


【解决方案1】:

我不确定这是否会解决您的问题,但这里有一个重构,它解决了 cmets 中指出的问题,以及您的代码中的一些其他问题。

  • 不要使用循环变量。 (反正也没用过。)
  • 不要将同一个子进程运行三次。
  • 避免无缘无故的shell=True
  • 为了一致性和正确性,首选/list 而不是-list

我已经删除了你的 cmets 和我的内联 cmets,解释了具体发生了什么变化。

# Only necessary in Python 2, you really should be using Python 3 now
#from __future__ import print_function
# Only used in os.system('cls') which was commented out (for good reasons I presume)
#import os
import psutil
import subprocess
from time import sleep # see below

# Simply loop forever; break when done
while True:
    # Remove gratuitous parentheses
    CPUload = psutil.cpu_percent(interval=4)
    RAMload = psutil.virtual_memory().percent

    # Use .format() to inline a string
    print("CPU Load: {}%".format(CPUload)))
    print("RAM Load: {}%".format(RAMload))

    # Only run subprocess once; use /list pro -list; don't use shell=True
    pcfg = subprocess.check_output(["powercfg", "/list"], shell=False).split('\n')
    # Additional refactoring below ########
    firstplan = pcfg[3]
    secondplan = pcfg[4]
    thirdplan = pcfg[5]

    # Get rid of wacky parentheses    
    firstplanID = firstplan.split(": ")[1].split("  (")[0]
    secondplanID = secondplan.split(": ")[1].split("  (")[0]
    thirdplanID = thirdplan.split(": ")[1].split("  (")[0]

    activeplan = subprocess.check_output(["powercfg", "/getactivescheme"])
    # Get rid of wacky parentheses
    activeplanNAME = activeplan.split("(")[1].split(")")[0]    
    firstplanNAME = firstplan.split("(")[1].split(")")[0]
    secondplanNAME = secondplan.split("(")[1].split(")")[0]
    thirdplanNAME = thirdplan.split("(")[1].split(")")[0]

    if "High performance" in firstplanNAME:
        HighPerformance = firstplanNAME
        HighPerformanceID = firstplanID

    if "High performance" in secondplanNAME:
        HighPerformance = secondplanNAME
        HighPerformanceID = secondplanID

    if "High performance" in thirdplanNAME:
        HighPerformance = thirdplanNAME
        HighPerformanceID = thirdplanID

    if "Power saver" in firstplanNAME:
        PowerSaver = firstplanNAME
        PowerSaverID = firstplanID

    if "Power saver" in secondplanNAME:
        PowerSaver = secondplanNAME
        PowerSaverID = secondplanID

    if "Power saver" in thirdplanNAME:
        PowerSaver = thirdplanNAME  
        PowerSaverID = thirdplanID

    # Additional refactoring ends    

    if activeplanNAME == PowerSaver:
        print("Active plan: Power saver")
    # prefer if / elif / else over nested if
    elif activeplanNAME == HighPerformance:
        print("Active plan: High Performance")
    else:
        # What's this supposed to do? You are capturing, then discarding the output.
        # Perhaps you are looking for subprocess.check_call()?
        subprocess.check_output(["powercfg", "/s", HighPerformanceID])          

    if CPUload < 44:    
        # Combine conditions rather than nesting conditionals
        if RAMload > 90 and activeplanNAME == PowerSaver:
            # subprocess.check_call() here too?
            subprocess.check_output(["powercfg", "/s", HighPerformanceID])
            print("Switching to High Performance by RAM load...")       

        # Don't check if CPUload < 44: again
        # Instead, just stay within this indented block
        # Combine conditions
        elif RAMload < 90 and activeplanNAME == HighPerformance:
            # subprocess.check_call() here too?
            subprocess.check_output(["powercfg", "/s", PowerSaverID])
            print("Switching to Power saver...")                    

        # What if RAMload == 90?

    # Combine conditions
    if CPUload > 55 and activeplanNAME == PowerSaver:
        # subprocess.check_call() here too?
        subprocess.check_output(["powercfg", "/s", HighPerformanceID])
        print("Switching to High Performance...")

    # Maybe sleep between iterations?
    #sleep(1)

脚本当前运行一个相当紧凑的循环,您可能需要取消对最后一行的注释。

还是有很多重复的代码。您可能需要考虑进一步重构,将三个计划收集到一个数组中,其中每个对象都是一个字典,其成员名称标识您提取的不同属性。

    # Additional refactoring below ########
    activeplan = subprocess.check_output(["powercfg", "/getactivescheme"])
    activeplanNAME = activeplan.split("(")[1].split(")")[0]    

    plan = []
    for idx in range(3):
        raw = pcfg[3+idx]
        thisplan = {'raw': raw}
        thisplan['id'] = raw.split(": ")[1].split("  (")[0]
        thisplan['name'] = raw.split("(")[1].split(")")[0]

        if "High performance" in thisplan['name']:
            HighPerformance = thisplan['name']
            HighPerformanceID = thisplan['id']    

        if "Power saver" in thisplan['name']:
            PowerSaver = thisplan['name']
            PowerSaverID = thisplan['id']

        plan[idx] = thisplan    

现在您实际上可以更改HighPerformancePowerSaver 变量以仅记住idx,然后如果您愿意,您可以从带有plan[PowerSaverIdx]['name'] 和ID 的字典列表中提取名称@ 987654331@.

【讨论】:

  • 所以我发现了这个:stackoverflow.com/a/51706087/8142044 并修复了脚本在--noconsole 模式下以及在我取出stdout-subprocess.PIPE 后崩溃的问题。我为每个subprocess.check_output 放了这个。但是,该程序会每隔几秒钟闪烁一次命令提示符,这违背了后台运行进程的目的。
  • 抱歉,帮不上忙。我的解决方案是完全放弃 Windows,这是我做出的最佳决定之一。
  • 好的,谢谢你的帮助!我将开始一个新的问题。还有你用什么操作系统?
  • 一揽子建议是有问题的,但就我所做的而言,我更喜欢 Debian Linux。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 2020-10-30
  • 2017-04-01
  • 2022-01-11
  • 2011-06-11
相关资源
最近更新 更多