Python子进程中列表索引超出范围

3 投票
2 回答
4519 浏览
提问于 2025-04-18 07:01

我在使用来自这个回答的代码来调节某个程序(在这个例子中是BS.player)的音量时,遇到了调用get_master_volume函数的问题。我是在pyHook的“OnKeyboardEvent”里面使用它的,这部分代码是:

def get_master_volume():
    config.read('%s/config.cfg'%os.path.abspath(os.path.dirname(__file__)))
    proc = subprocess.Popen('%s sget Master '%config.get('main', input1), shell=True,    stdout=subprocess.PIPE) #config.get gives FULL and correct path to bsplayer.exe
    amixer_stdout = proc.communicate()[0].split('\n')[4]
    proc.wait()

    find_start = amixer_stdout.find('[') + 1
    find_end = amixer_stdout.find('%]', find_start)

    return float(amixer_stdout[find_start:find_end])

def set_master_volume(volume):
    config.read('%s/config.cfg'%os.path.abspath(os.path.dirname(__file__)))
    val = float(int(volume))

    proc = subprocess.Popen('%s sset Master '%config.get('main', input1) + str(val) +  '%', shell=True, stdout=subprocess.PIPE) #config.get gives FULL and correct path to bsplayer.exe
    proc.wait()

def OnKeyboardEvent(event):
    #nothing important
    if config.has_option('main', input1):
       set_master_volume(get_master_volume() - 1)
       print "New volume:", get_master_volume()

这是错误信息:

File "c:/Users/Amar/Documents/volume+/main.py", line 53, in get_master_volume
     amixer_stdout = proc.communicate()[0].split('\n')[4]
IndexError: list index out of range

有没有人能告诉我为什么会出现这个错误,是什么原因导致的(我该怎么做才能解决这个问题)?

最好的祝福!

编辑:

当我打印proc.communicate()时,出现了这些错误,这是什么意思呢?

proc.communicate:'C:\Program' is not recognized as an internal or external comma
nd,
operable program or batch file.
('', None)

编辑 2:

在我修复了配置中的一个错误(它指向了错误的bsplayer.exe路径)后,出现了新的错误:

 Traceback (most recent call last):
 File "c:\Python27\lib\site-packages\pyHook\HookManager.py", line 351, in Keybo
 ardSwitch
 return func(event)
 File "c:/Users/Amar/Documents/volume+/main.py", line 101, in OnKeyboardEvent
  set_master_volume(get_master_volume() - 1)
 File "c:/Users/Amar/Documents/volume+/main.py", line 54, in get_master_volume
  amixer_stdout = proc.communicate()[0].split('\n')[4]
 File "c:\Python27\lib\subprocess.py", line 798, in communicate
  stdout = _eintr_retry_call(self.stdout.read)
 File "c:\Python27\lib\subprocess.py", line 478, in _eintr_retry_call
  return func(*args)
 ValueError: I/O operation on closed file

2 个回答

1

让我来试着回答你这三个问题。

  1. IndexError

    File "c:/Users/Amar/Documents/volume+/main.py", line 53, in get_master_volume
        amixer_stdout = proc.communicate()[0].split('\n')[4]
    IndexError: list index out of range
    

    这个错误意味着你在尝试访问一个空的序列(这种情况不太可能),或者你在用 [4] 访问一个最多只有4个元素的序列,所以 [4] 这个位置是不存在的。

    显然,输出的行数少于4行,因此 proc.communicate()[0].split('\n') 中没有 [4] 这个位置。

    要么是程序的输出和预期不符,要么是其他地方出了问题。

  2. proc.communicate

    proc.communicate:'C:\Program' is not recognized as an internal or external comma
    

    这似乎表明你的命令行调用有问题。

    config.get('main', input1) 似乎包含了完整的路径,而这个路径在 C:\Program Files 下。所以你应该在正确的位置用 " 把程序路径包起来。

  3. I/O on closed file

    第三个问题可以通过检查调用的记录来解决。在这里我们可以看到调用的方式有问题:communicate() 的调用失败,因为子进程的 stdout 已经关闭了。

    我不太清楚为什么会这样;也许你是调用了 communicate() 两次?第一次调用时,它已经关闭了,而当你尝试第二次调用时,就失败了,因为 stdout 已经被读取完了。

2

呃……我刚刚看了你提到的另一个问题,发现你完全忽略了一点。你试图把发给Linux程序amixer的参数用在Windows程序bsplayer.exe上,这两者根本没有关系。这几乎不可能成功!这就是它不工作的原因。参考一下另一个问题:

proc = subprocess.Popen('/usr/bin/amixer sget Master', shell=True, stdout=subprocess.PIPE)

所以问题的第一部分是解决你调用bsplayer.exe的问题,但你实际上想要的功能是行不通的。


你应该尝试以下方法来调用子进程:

proc = subprocess.Popen([config.get('main', input1), 'sget', 'Master'], stdout=subprocess.PIPE)

因为,虽然我在Windows的Python方面经验不多,但我觉得用shell调用可能不会按预期工作,试着把你的路径当作参数来解释。不过,也许如果你把参数用引号括起来,它可能会有效:

proc = subprocess.Popen('"%s" sget Master '%config.get('main', input1), shell=True,    stdout=subprocess.PIPE)

我得出这个结论的原因是:

'C:\Program' is not recognized as an internal or external command,

通常这意味着bsplayer.exe的路径在第一个空格处被截断了。


编辑:

你能运行以下命令,并把输出结果加到你的问题中吗?

基本上:

  • 我把proc.wait()放在了通信之前
  • 把选择第四行输出的步骤移到了第二步
  • 添加了输出列表的打印

我想知道你遇到的错误是因为communicate()不工作,还是因为输出的行数少于四行。

所以以下版本解决了你的调用问题:

def get_master_volume():
    config.read('%s/config.cfg'%os.path.abspath(os.path.dirname(__file__)))
    p = [config.get('main', input1), 'sget', 'Master']
    print("PROG:", p)
    proc = subprocess.Popen(p, stdout=subprocess.PIPE)

    proc.wait()

    amixer_stdout = proc.communicate()[0].split('\n')
    print("OUTPUT", amixer_stdout)
    amixer_stdout = amixer_stdout[4]

    find_start = amixer_stdout.find('[') + 1
    find_end = amixer_stdout.find('%]', find_start)

    return float(amixer_stdout[find_start:find_end])

不过它什么也不输出。

你的命令真的有效吗?!你有没有尝试在cmd中运行:

"C:\Program Files (x86)\Webteh\BSPlayer\bsplayer.exe" sget Master

看看它是否能工作?

撰写回答