Bash process substitution in Python with Popen
Question:
I’m attempting to create a looped video file by calling ffmpeg from the python subprocess library. Here’s the part that’s giving me problems:
import subprocess as sp
sp.Popen(['ffmpeg', '-f', 'concat', '-i', "<(for f in ~/Desktop/*.mp4; do echo "file '$f'"; done)", "-c", "copy", "~/Desktop/sample3.mp4"])
With the above code I’m getting the following error:
<(for f in /home/delta/Desktop/*.mp4; do echo "file '$f'"; done): No such file or directory
I did find a similarly phrased question here. But I’m not sure how the solution might apply to solving my issue.
Answers:
Following the advice in the comments and looking elsewhere I ended up changing the code to this:
sp.Popen("ffmpeg -f concat -i <(for f in ~/Desktop/*.mp4; do echo "file '$f'"; done) -c copy ~/Desktop/sample3.mp4",
shell=True, executable="/bin/bash")
–which works fine. – moorej
If you need to parameterize input and output files, consider breaking out your parameters:
# sample variables
inputDirectory = os.path.expanduser('~/Desktop')
outputDirectory = os.path.expanduser('~/dest.mp4')
sp.Popen(['''ffmpef -f concat -i <(for f in "$1"/*; do
echo "file '$f'";
done) -c copy "$2" ''',
bash, # this becomes $0
inputDirectory, # this becomes $1
outputDirectory, # this becomes $2
], shell=True, executable="/bin/bash")
…as this ensures that your code won’t do untoward things even when given an input directory with a hostile name like /uploads/$(rm -rf ~)'$(rm -rf ~)'
. (ffmpeg is likely to fail to parse an input file with such a name, and if there’s any video you don’t want included in the current working directory but we’d need to know the escaping rules it uses to avoid that; but it’s far better for ffmpeg to fail than to execute arbitrary code).
I’m attempting to create a looped video file by calling ffmpeg from the python subprocess library. Here’s the part that’s giving me problems:
import subprocess as sp
sp.Popen(['ffmpeg', '-f', 'concat', '-i', "<(for f in ~/Desktop/*.mp4; do echo "file '$f'"; done)", "-c", "copy", "~/Desktop/sample3.mp4"])
With the above code I’m getting the following error:
<(for f in /home/delta/Desktop/*.mp4; do echo "file '$f'"; done): No such file or directory
I did find a similarly phrased question here. But I’m not sure how the solution might apply to solving my issue.
Following the advice in the comments and looking elsewhere I ended up changing the code to this:
sp.Popen("ffmpeg -f concat -i <(for f in ~/Desktop/*.mp4; do echo "file '$f'"; done) -c copy ~/Desktop/sample3.mp4",
shell=True, executable="/bin/bash")
–which works fine. – moorej
If you need to parameterize input and output files, consider breaking out your parameters:
# sample variables
inputDirectory = os.path.expanduser('~/Desktop')
outputDirectory = os.path.expanduser('~/dest.mp4')
sp.Popen(['''ffmpef -f concat -i <(for f in "$1"/*; do
echo "file '$f'";
done) -c copy "$2" ''',
bash, # this becomes $0
inputDirectory, # this becomes $1
outputDirectory, # this becomes $2
], shell=True, executable="/bin/bash")
…as this ensures that your code won’t do untoward things even when given an input directory with a hostile name like /uploads/$(rm -rf ~)'$(rm -rf ~)'
. (ffmpeg is likely to fail to parse an input file with such a name, and if there’s any video you don’t want included in the current working directory but we’d need to know the escaping rules it uses to avoid that; but it’s far better for ffmpeg to fail than to execute arbitrary code).