【问题标题】:Passing double quote shell commands in python to subprocess.Popen()?将python中的双引号shell命令传递给subprocess.Popen()?
【发布时间】:2013-02-02 10:39:54
【问题描述】:

我一直在尝试在命令行中围绕 ffmpeg 的 "concat:file1|file2" 参数传递一个仅使用文字双引号的命令。

但是,我无法使用 subprocess.Popen() 在 python 中完成这项工作。任何人都知道如何将引号传递给 subprocess.Popen?

代码如下:

command = "ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4"

output,error = subprocess.Popen(command, universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate()

当我这样做时,ffmpeg 除了在 concat 段周围加上引号外,不会采取任何其他方式。有没有办法成功地将这一行传递给 subprocess.Popen 命令?

【问题讨论】:

  • 不只是在你的命令周围使用“单引号”吗?如果您在终端上完全按照您所写的方式输入命令,那么处理这些引号的将是 shell,而不是 ffmpeg!
  • 那是真的代码吗?当我在 Python 终端中输入command = "ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4" 时,我得到SyntaxError: invalid syntax...为什么,有趣...似乎子进程即使没有调用它也会做坏事...
  • 您的 Python 代码有一个简单的语法错误,您可以使用如下的单引号进行纠正:command = 'ffmpeg -i "concat:1.ts|2.ts" -vcode copy -acodec copy temp.mp4'

标签: python subprocess


【解决方案1】:

您问题中的这行代码不是有效的 Python 语法:

command = "ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4"

如果你的 Python 文件中只有这一行,你会得到一个语法错误。用双引号括起来的字符串文字中不能有双引号,除非它们用反斜杠转义。因此,您可以通过将其替换为来修复该行:

command = "ffmpeg -i \"concat:1.ts|2.ts\" -vcodec copy -acodec copy temp.mp4"

解决这一行的另一种方法是在 Python 中对字符串文字使用单引号,这样当字符串本身包含双引号时 Python 就不会混淆:

command = 'ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4'

修复语法错误后,您可以使用subprocess 解决该问题,如this answer 中所述。我还写了this answer to explain a helpful mental model for subprocess in general

【讨论】:

    【解决方案2】:

    任何遭受这种痛苦的人。它也适用于用引号括起来的参数。

    params = ["ls", "-la"]
    subprocess.check_output(" ".join(params), shell=True)
    

    【讨论】:

      【解决方案3】:

      我一直在处理类似的问题,运行一个相对复杂的 通过 ssh 命令。它也有多个双引号和单引号。因为 我正在通过pythonsshpowershell 等传递命令。

      如果您可以将命令转换为 shell 脚本,然后运行 shell脚本通过subprocess.call/Popen/run,这些问题就会消失。

      因此,根据您使用的是 windows 还是 linux 或 mac,请将 在 shell 脚本中跟随(script.shscript.bat

      ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4
      

      然后就可以运行了

      import subprocess; subprocess.call(`./script.sh`; shell=True)
      

      无需担心单引号等

      【讨论】:

      • 使用 Popen 将参数发送到 shell 脚本也可能会有所帮助,例如:subprocess.Popen(["./cheddar.sh", "17"], stdout=subprocess.PIPE)。这会将数字 17 作为第一个参数发送到 cheddar.sh 脚本。请注意,shell 脚本中的参数可以作为 $1、$2、$3、... $n 访问。因此,在本例中,您可以将数字 17 获取为 $1。
      【解决方案4】:

      还在处理包含空格且不想使用 shell=True 的字符串参数。

      解决方案是对内部字符串使用双引号。

      args = ['salt', '-G', 'environment:DEV', 'grains.setvals', '{"man_version": "man-dev-2.3"}']
      
      try:
          p = subprocess.Popen(args, stdin=subprocess.PIPE
                                   , stdout=subprocess.PIPE
                                   , stderr=subprocess.PIPE
                              )
          (stdin,stderr) = p.communicate()
      except (subprocess.CalledProcessError, OSError ) as err:
          exit(1)
      if p.returncode != 0:
          print("Failure in returncode of command:")
      

      【讨论】:

      • 有什么理由使用双引号吗?据我了解,Python 中的单引号和双引号没有区别——只有当你想将一个组放在另一个组中时。但这个答案已经存在。
      【解决方案5】:

      使用单引号'around the "whole pattern"' 自动转义双精度或显式"escape the \"double quotes\""。您的问题与Popen 本身无关。

      为了记录,我遇到了一个问题,特别是传递给Popen 的基于list 的命令不会在全局模式周围保留正确的双引号(即在Windows 下的accepted answer)。在将列表传递给Popen 之前,将列表加入到带有' '.join(cmd) 的字符串中解决了这个问题。

      【讨论】:

      • 这是对我有用的答案。我必须做subprocess.run(" ".join(cmd), shell=True) 才能让它工作。没有其他工作。
      • 我使用相同的方法将非代理主机 (-N) 传递给 JMeter "*.local|localhost"。我正在使用 python 向用户请求代理密码。
      【解决方案6】:

      这适用于 python 2.7.3 管道 stderr 到 stdout 的命令已更改,因为旧版本的 python:

      把它放在一个名为 test.py 的文件中:

      #!/usr/bin/python
      import subprocess
      
      command = 'php -r "echo gethostname();"'
      p = subprocess.Popen(command, universal_newlines=True, shell=True, 
      stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
      text = p.stdout.read()
      retcode = p.wait()
      print text
      

      调用它:

      python test.py
      

      它会打印我的主机名,即 apollo:

      apollo
      

      阅读子流程手册:http://docs.python.org/2/library/subprocess.html

      【讨论】:

        【解决方案7】:

        我建议使用列表形式的调用而不是引用的字符串版本:

        command = ["ffmpeg", "-i", "concat:1.ts|2.ts", "-vcodec", "copy",
                   "-acodec", "copy", "temp.mp4"]
        output,error  = subprocess.Popen(
                            command, universal_newlines=True,
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
        

        这更准确地表示了将要传递给最终进程的确切参数集,并且无需弄乱 shell 引用。

        也就是说,如果您绝对想使用纯字符串版本,只需使用不同的引号(和shell=True):

        command = 'ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4'
        output,error  = subprocess.Popen(
                            command, universal_newlines=True, shell=True,
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
        

        【讨论】:

        • 在 Unix 上(至少),如果你要传递一个字符串,你需要使用 shell=True。否则,据我了解,python 将在您的路径中查找名为 ffmpeg -i "concat.... 的程序——该程序将不存在。
        • 感谢您花时间回答,不幸的是,实际的 ffmpeg 命令需要文字引号传递 concat:file1|file2 参数,所以我正在寻找一种方法将文字引号从 python 子进程传递给 ffmpeg。弹出命令
        • @FightFireWithFire 最后一个版本不适合你吗?我相信它可以满足您的需求。
        • 在第一个版本中使用'"concat:1.ts|2.ts"'
        • @FightFireWithFire:如果您需要两种引号,那么您可以使用三引号 Python 文字:`cmd = r"""cmd "concat:1.ts|2.ts" $' \t' ..."""
        猜你喜欢
        • 2018-11-17
        • 2013-04-19
        • 2018-01-27
        • 2020-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-14
        相关资源
        最近更新 更多