【问题标题】:Bash script will not run with subprocess in PythonBash 脚本不会与 Python 中的子进程一起运行
【发布时间】:2023-03-24 07:27:01
【问题描述】:

出于某种原因,无论我尝试了多少变体,我似乎都无法执行我编写的 bash 脚本。命令字在终端中 100% 没问题,但是当我尝试使用子进程调用它时,它什么也不返回。

from os import listdir
import subprocess

computer_name = 'homedirectoryname'

moviefolder = '/Users/{}/Documents/Programming/Voicer/Movies'.format(computer_name)

string = 'The lion king'

for i in listdir(moviefolder):
    title = i.split('.')
    formatted_title = title[0].replace(' ', '\ ')

    if string.lower() == title[0].lower():
        command = 'vlc {}/{}.{}'.format(moviefolder, formatted_title, title[1])

        subprocess.call(["/usr/local/bin",'-i','-c', command], stdout=subprocess.PIPE,
                                                        stderr=subprocess.PIPE, shell=True)
    else:
        continue

bash 可执行文件如下所示:

#/bin/bash

func() {
    open -a /Applications/VLC.app/Contents/MacOS/VLC $1
}

我哪里出错了?

【问题讨论】:

  • 不确定问题,但subprocess.call 文档说不要将PIPEstdoutstderr 一起使用
  • @MoinuddinQuadri 如果我把它们拿出来,它会在终端返回-i: /usr/local/bin: is a directory
  • 你为什么还要尝试调用一个shell? shell 是一个位于操作系统之上的实用程序,它为您提供一个用户界面来启动程序。你不需要那个,只需启动 VLC。
  • 使用check_call,这样你就可以看到你的调用是否成功。 call 不检查任何内容。
  • 错误信息正确:/usr/local/bin目录。您不希望能够将其作为命令运行。

标签: python bash subprocess


【解决方案1】:

您应该直接致电open

import os
import subprocess

computer_name = 'homedirectoryname'

moviefolder = '/Users/{}/Documents/Programming/Voicer/Movies'.format(computer_name)

string = 'The lion king'

for filename in os.listdir(moviefolder):
    title = filename.split('.')

    if string.lower() == title[0].lower():
        subprocess.call(['open', '-a', '/Applications/VLC.app/Contents/MacOS/VLC', os.path.join(moviefolder, filename)])

【讨论】:

  • 哇。我不知道为什么我没有想到这一点。呃。我用这个作为我的解决方案。谢谢!
【解决方案2】:

由于您使用的是shell=True,因此命令必须是字符串:

在 shell=True 的 Unix 上,shell 默认为 /bin/sh。如果 args 是 string,字符串指定要通过shell执行的命令。 这意味着字符串必须完全按照它的格式进行格式化 在 shell 提示符下键入时。这包括,例如,引用或 反斜杠转义带有空格的文件名。如果 args 是 序列,第一项指定命令字符串,任何 附加项将被视为 shell 的附加参数 本身。 (docs)

【讨论】:

  • 绝对可以使用shell=True 传递一个数组——该数组的第一个元素应该是一个脚本,随后的元素参数应该是该脚本。在安全方面,调用subprocess.Popen(['script involving "$1", '_', python_variable]) 比调用subprocess.Popen(['script involving %s' % (python_variable,)]) 更好。
  • @CharlesDuffy 这很奇怪,因为引用的文档明确指出,附加项目被解释为 shell 的 args 而不是“脚本”
  • 如果您有任何问题,可以在这里阅读实现——shell=True 字面上只是将['sh', '-c'] 添加到参数列表(如果传递了字符串,则为带有字符串的单项列表,或用户给出的文字列表)。
  • ...当sh -c '...script here...' 'argument 0' 'argument 1' 'argument 2' 运行时,您的后续参数是$0$1$2 等,在-c 之后立即传递的脚本上下文中. (这意味着如果该脚本不引用其后续参数,它们将被完全忽略)。
  • ...所以,是的,后续项目作为参数传递给 shell,但 shell 将它们作为参数传递给脚本。
【解决方案3】:

就像你在评论中提到的那样,当你从 shell 正确捕获错误时,你会得到/usr/local/bin: is a directory(并取出错误的shell=True;或者相应地重构命令行以适合这种用法,即通过一个字符串而不是一个列表)。

只是为了说明这一点,您正在尝试使用一些选项运行命令/usr/local/bin;但当然,这不是一个有效的命令;所以这失败了。

您似乎要运行的实际脚本将声明一个函数然后退出,这会导致函数的定义再次丢失,因为运行执行此函数声明的 shell 的子进程现已终止并释放所有其资源返回系统。

也许您不应该只是退后几步,并解释您实际上想要完成的事情;但实际上,这应该是一个新的、单独的问题。

假设您实际上是在尝试运行vlc,并且还猜测了其他一些事情,也许您确实想要

subprocess.call(['vlc','{}/{}.{}'.format(moviefolder, formatted_title, title[1]),
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

如果您的PATH 正确,则无需显式指定/usr/local/bin/(如果您的PATH 错误,请在之前的代码中更正它,而不是为您要调用的可执行文件硬编码目录)。

【讨论】:

    【解决方案4】:

    /usr/local/bin 是一个目录。您不能像运行命令一样运行目录。

    无论如何,在您的命令中的任何位置都没有/usr/local/bin。省略shell=True,并显式调用vlc

    subprocess.call([
      'vlc',
      '{}/{}.{}'.format(moviefolder, formatted_title, title[1])
    ])
    

    【讨论】:

      【解决方案5】:

      subprocess.call 中使用shell=True 时,如果命令参数是一个序列,那么序列的第一个元素需要是命令,其余的被视为shell 本身的参数。

      所以,应该这样做:

      subprocess.call(["/usr/local/bin/{}".format(command), '-i','-c'], shell=True, ...)
      

      否则,您可以将命令设为字符串。

      示例:

      In [20]: subprocess.call(["cat spamegg", "-i", "-c"], shell=True)
      foobar
      

      【讨论】:

      • 试过这个,但没用。什么也没回。 command = 'vlc {}/{}.{}'.format(moviefolder, formatted_title, title[1])subprocess.call(["/usr/local/bin/{}".format(command), '-i','-c'], shell=True)
      • @AntonioNogueras 你期待什么?你为什么要这样运行vlc?然后尝试cvlc。另外,尝试使用其他命令。
      • 这仍然是错误的,因为vlc 的参数应该是命令列表中的一个单独字符串,或者代码应该重构为使用shell=True 和一个单独的命令字符串。
      • 谢谢;这与 Charles 对本页另一个答案的 cmets 押韵很好,尽管它必须被视为未记录的行为,很可能在未来的版本中发生变化。
      最近更新 更多