66

I've been trying to pass a command that works only with literal double quotes in the commandline around the "concat:file1|file2" argument for ffmpeg.

I cant however make this work from python with subprocess.Popen(). Anyone have an idea how one passes quotes into subprocess.Popen?

Here is the code:

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()

When I do this, ffmpeg won't take it any other way other than quotes around the concat segement. Is there a way to successfully pass this line to subprocess.Popen command?

Max
  • 221
  • 5
  • 20
Fight Fire With Fire
  • 1,626
  • 3
  • 19
  • 28
  • Doesn't simply using 'single quotes' around your command work? If you are entering the command on the terminal exactly as you have written, it would be the shell that handles those quotes, not ffmpeg! – wim Feb 18 '13 at 03:40
  • Is that *really* the code? When I type `command = "ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4"` in Python terminal, I get ``SyntaxError: invalid syntax``... Why, interesting... Seems subprocess makes its evil dids even without envoking that... – Yaroslav Nikitenko Jan 31 '20 at 19:30
  • Your Python code has a simple syntax error, which you can correct by using single quotes like this: `command = 'ffmpeg -i "concat:1.ts|2.ts" -vcode copy -acodec copy temp.mp4'` – Flimm Jan 11 '22 at 14:50

7 Answers7

69

I'd suggest using the list form of invocation rather than the quoted string version:

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()

This more accurately represents the exact set of parameters that are going to be passed to the end process and eliminates the need to mess around with shell quoting.

That said, if you absolutely want to use the plain string version, just use different quotes (and 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()
Amber
  • 507,862
  • 82
  • 626
  • 550
  • 4
    And on Unix (at least), you need to use `shell=True` if you're going to pass a string. Otherwise, as I understand it, python will look for a program called `ffmpeg -i "concat....` in your path -- Which won't exist. – mgilson Feb 18 '13 at 03:21
  • thanks for taking the time to answer, unforunately the actual ffmpeg command needs literal quotes passes around the concat:file1|file2 argument so I'm seeking a way to pass literal quotes to ffmpeg from python subprocess.Popen command – Fight Fire With Fire Feb 18 '13 at 03:32
  • @FightFireWithFire Does the last version not work for you? I believe it does what you want. – Matt Feb 18 '13 at 04:50
  • In the first version use '"concat:1.ts|2.ts"' – tdelaney Feb 18 '13 at 06:23
  • 1
    @FightFireWithFire: if you need both kinds of quotes then you could use a triple-quotes Python literal: `cmd = r"""cmd "concat:1.ts|2.ts" $'\t' ...""" – jfs Dec 08 '13 at 17:47
  • I've used this successfully over time. Thank you. Just a note i don't use shell=True and you can pass single quotes this way anyway you want – Fight Fire With Fire Feb 11 '15 at 00:50
  • "eliminates the need to mess around with shell quoting" How can this be true? For example `$ echo *.sh` is decidedly different from `$ echo "*.sh"`. – Him Oct 29 '18 at 18:14
  • The first one in this answer doesn't work. `command = ["calc.exe", "-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()` Now look in task manager at the command line, or at cmd.exe wmic output `wmic process where caption="calc.exe" get commandline | findstr calc` shows `calc.exe -i concat:1.ts|2.ts -vcodec copy -acodec copy temp.mp4` No quotes around the concat part. – barlop Aug 13 '22 at 04:01
  • As a further corollary, on modern Python you want to avoid `Popen` whenever you can; with `subprocess.check_call` this is a one-liner which doesn't require the `communicate()` song and dance. – tripleee Nov 10 '22 at 12:25
11

Either use single quotes 'around the "whole pattern"' to automatically escape the doubles or explicitly "escape the \"double quotes\"". Your problem has nothing to do with Popen as such.

Just for the record, I had a problem particularly with a list-based command passed to Popen that would not preserve proper double quotes around a glob pattern (i.e. what was suggested in the accepted answer) under Windows. Joining the list into a string with ' '.join(cmd) before passing it to Popen solved the problem.

Community
  • 1
  • 1
j08lue
  • 1,647
  • 2
  • 21
  • 37
  • 3
    This is the answer that worked for me. I had to do `subprocess.run(" ".join(cmd), shell=True)` to get it working. Nothing else worked. – Gillespie May 11 '17 at 16:24
  • I used the same for passing nonproxy hosts (`-N`) to JMeter `"*.local|localhost"`. I'm using python to request user for a proxy password. – Betlista Mar 09 '21 at 07:11
8

This works with python 2.7.3 The command to pipe stderr to stdout has changed since older versions of python:

Put this in a file called 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

Invoke it:

python test.py

It prints my hostname, which is apollo:

apollo

Read up on the manual for subprocess: http://docs.python.org/2/library/subprocess.html

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
2

I have been working with a similar issue, with running a relatively complex command over ssh. It also had multiple double quotes and single quotes. Because I was piping the command through python, ssh, powershell etc.

If you can instead just convert the command into a shell script, and run the shell script through subprocess.call/Popen/run, these issues will go away.

So depending on whether you are on windows or on linux or mac, put the following in a shell script either (script.sh or script.bat)

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

Then you can run

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

Without having to worry about single quotes, etc.

empty
  • 5,194
  • 3
  • 32
  • 58
alpha_989
  • 4,882
  • 2
  • 37
  • 48
  • It may also be helpful, using Popen, to send arguments to the shell script, like so: ``subprocess.Popen(["./cheddar.sh", "17"], stdout=subprocess.PIPE)``. This will send the number ``17`` as the first argument to the ``cheddar.sh`` script. Note that arguments within shell scripts are accessible as $1, $2, $3, ... $n. So in this example, you can grab the number ``17`` as $1. – kmiklas Jan 09 '19 at 21:45
  • The backticks around `./script.sh` are a syntax error in modern Python. – tripleee Jan 26 '22 at 07:11
1

This line of code in your question isn't valid Python syntax:

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

If you had a Python file with just this line in it, you would get a syntax error. A string literal surrounded with double quotes can't have double quotes in them unless they are escaped with a backslash. So you could fix that line by replacing it with:

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

Another way to fix this line is to use single quotes for the string literal in Python, that way Python is not confused when the string itself contains a double quote:

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

Once you have fixed the syntax error, you can then tackle the issue with using subprocess, as explained in this answer. I also wrote this answer to explain a helpful mental model for subprocess in general.

Flimm
  • 136,138
  • 45
  • 251
  • 267
0

Also struggling with a string argument containing spaces and not wanting to use the shell=True.

The solution was to use double quotes for the inside strings.

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:")
Pieter
  • 1,916
  • 17
  • 17
  • Is there any reason to use double quotes? As I understand, there is no difference between single and double quotes in Python - only if you want to put one group inside another. But that answer already exists. – Yaroslav Nikitenko Jan 31 '20 at 19:26
0

Anybody suffering from this pain. It also works with params enclosed with quotation marks.

params = ["ls", "-la"]
subprocess.check_output(" ".join(params), shell=True)
Wolfgang Leon
  • 695
  • 9
  • 20
  • Downvote: Why would you force `shell=True` when what you had was fine without it? `subprocess.check_output(params)` is the way to go here. See also [Actual meaning of `shell=True` in subprocess](https://stackoverflow.com/questions/3172470/actual-meaning-of-shell-true-in-subprocess) – tripleee Nov 10 '22 at 12:21
  • There is still some cases in which `shell=True` is just the quickest solution (for instance, I did not figure out how to deal with multiple quotations in an argument without a separate bash script) – F1iX Jun 08 '23 at 01:08