Gordon Fontenot
Gordon Fontenot

Reputation: 1380

Properly using subprocess.PIPE in python?

I'm trying to use subprocess.Popen to construct a sequence to grab the duration of a video file. I've been searching for 3 days, and can't find any reason online as to why this code isn't working, but it keeps giving me a blank result:

import sys
import os
import subprocess

def main():
  the_file = "/Volumes/Footage/Acura/MDX/2001/Crash Test/01 Acura MDX Front Crash.mov"
  ffmpeg = subprocess.Popen(['/opt/local/bin/ffmpeg', '-i', the_file], stdout = subprocess.PIPE, )
  grep = subprocess.Popen(['grep', 'Duration'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, )
  cut = subprocess.Popen(['cut', '-d', ' ', '-f', '4'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, )
  sed = subprocess.Popen(['sed', 's/,//'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, )

  duration = sed.communicate()
  print duration

if __name__ == '__main__':
  main()

Upvotes: 14

Views: 44930

Answers (4)

KevB
KevB

Reputation: 251

As others have pointed out, you need to pass the PIPE from one process to the next. The stdout (PIPE) from one process becomes the stdin for the following task.

Something like this (starting from your example):

import sys
import os
import subprocess

def main():
  the_file = "/Volumes/Footage/Acura/MDX/
              2001/Crash Test/01 Acura MDX Front Crash.mov"
  ffmpeg = subprocess.Popen(['/opt/local/bin/ffmpeg', '-i', the_file],
                            stdout = subprocess.PIPE)
  grep = subprocess.Popen(['grep', 'Duration'], 
                          stdin = ffmpeg.stdout, stdout = subprocess.PIPE)
  cut = subprocess.Popen(['cut', '-d', ' ', '-f', '4'],
                         stdin = grep.stdout, stdout = subprocess.PIPE)
  sed = subprocess.Popen(['sed', 's/,//'],
                         stdin = cut.stdout, stdout = subprocess.PIPE)

  duration = sed.communicate()[0]
  print duration

if __name__ == '__main__':
  main()

Upvotes: 25

ghostdog74
ghostdog74

Reputation: 342263

stderr needs to be redirected to stdout. Also, there's no need to call other tools like cut/sed etc. do your string manipulation in Python

import subprocess
....
the_file = "/Volumes/Footage/Acura/MDX/2001/Crash Test/01 Acura MDX Front Crash.mov"
ffmpeg = subprocess.Popen(['/usr/bin/ffmpeg', '-i', the_file], stderr=subprocess.STDOUT,stdout = subprocess.PIPE )
out, err = ffmpeg.communicate()
if "Duration" in out:
    print out[out.index("Duration"):].split()[1]

If Python is not a must, you can use the shell directly.

the_file="/Volumes/Footage/Acura/MDX/2001/Crash Test/01 Acura MDX Front Crash.mov"
ffmpeg -i "$file" 2>&1 | awk '/Duration/{print $2}'

Upvotes: 15

Alex Martelli
Alex Martelli

Reputation: 881497

Python can't "build a whole pipeline" in this way -- it could delegate the task to the shell, or glue it up more directly using the stdout attributes of previous subprocess objects in the line, but there's really no reason for that in this specific case, since you can code it directly in Python pretty easily. E.g.:

  ffmpeg = subprocess.Popen(['/opt/local/bin/ffmpeg', '-i', the_file],
                            stdout=subprocess.PIPE)
  for line in ffmpeg.stdout:
    if 'Duration' not in line: continue
    fields = line.split()
    duration = fields[4].replace(',', '')
    break

Upvotes: 4

Aaron Digulla
Aaron Digulla

Reputation: 328556

Using subprocess.PIPE will not magically wire the correct pipes for you.

You must pass the output pipe of the first process as the value for the parameter stdin of the second process. See the docs for an example.

Upvotes: 14

Related Questions