为什么 subprocess.check_output 行会导致我的脚本崩溃?

Why is this subprocess.check_output line crashing my script?

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

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行。

有关更多信息,请向下滚动到评论和答案。

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

  • 不要使用循环变量。 (反正没用过。)
  • 不要 运行 相同的子进程三次。
  • 避免不必要的shell=True
  • 为了一致性和可能的​​正确性,/list 优于 -list

我已经删除了你的评论和我的内联评论,解释了到底发生了什么变化。

# 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 plan[PowerSaverIdx]['id'].