如何正确传递子进程参数

How to correctly pass subprocess arguments

我正在尝试自动连接一个包含(格式正确的)mp4 视频文件的文件夹。

(我的问题的这个编辑版本将问题减少到我的最低困惑水平。原始标题询问 subprocess.callsubprocess.run 之间的差异,但事实证明问题出在其他地方.)

为什么

subprocess.call('ffmpeg -hide_banner -loglevel error -i movie1.mp4 -i movie2.mp4 -i credits.mp4 \
-filter_complex "[0:v:0] [0:a:0] [1:v:0] [1:a:0][2:v:0][2:a:0]concat=n=3:v=1:a=1[outv][outa]"  \
-map "[outv]" -map "[outa]" "output.mp4"',shell=True)

工作正常(其中 s 是一串输入,count 是输入的数量),但是

#python3 
#makeFinalExample.py

import subprocess

s = '-i movie1.mp4 -i movie2.mp4 -i credits.mp4'
count = 3
print(f's prints out as: {s}')


commandList = ['ffmpeg',
                '-hide_banner',
                '-loglevel',
                'error',
                #str(s),
                '{0}'.format(s),
                '-filter_complex',
                "[0:v:0][0:a:0][1:v:0][1:a:0]concat=n={0}:v=1:a=1[outv][outa]".format(count),
                '-map',
                "[outv]",
                '-map',
                "[outa]",
                "output.mp4"]
print(f'the command list prints out as {commandList}')
subprocess.run(commandList)

收到错误(无论字符串是作为 str(s) 提供的,还是以显示的格式...

Unrecognized option 'i movie1.mp4 -i movie2.mp4 -i credits.mp4'.
Error splitting the argument list: Option not found

这是输入字符串的打印输出

-i movie1.mp4 -i movie2.mp4 -i credits.mp4

这是命令列表的打印输出

['ffmpeg', '-hide_banner', '-loglevel', 'error', '-i movie1.mp4 -i movie2.mp4 -i credits.mp4', '-filter_complex', '[0:v:0][0:a:0][1:v:0][1:a:0]concat=n=3:v=1:a=1[outv][outa]', '-map', '[outv]', '-map', '[outa]', 'output.mp4']

这与 subprocess.callsubprocess.run 无关,不同之处在于您在第一种情况下使用 shell=True,而在第二种情况下则没有。这两个函数在这方面和几乎所有其他方面的行为完全相同(subprocess.run 是一个较新的函数,它支持很多很多选项,returns 是一个更有用的对象,但以其最基本的形式,它执行完全相同的工作,使用相同的 API).

问题是您需要将字符串 s 拆分为标记,就像拆分所有其他命令行参数一样(错误消息实际上揭示了这一点,但我想您必须知道要寻找什么才能抓住它)。当您省略 shell=True 时,每个选项都需要是一个单独的列表项,例如

[ ..., '-i', 'movie1.mp4', '-i', 'movie2.mp4', '-i', 'credits.mp4', ...]

函数shlex.split()可以帮助您正确地将命令拆分为标记:

commandList = [
    'ffmpeg',
    '-hide_banner',
    '-loglevel', 'error',
    *shlex.split(s),
    '-filter_complex',
    "[0:v:0][0:a:0][1:v:0][1:a:0]concat=n={0}:v=1:a=1[outv][outa]".format(count),
    '-map', "[outv]",
    '-map', "[outa]",
    "output.mp4"]

但是您的函数的更好设计可能是允许用户仅传入一个输入视频文件名列表,然后从那里获取。 (那么我想 count 也不需要明确指定;它只是 len(inputfilenames) 的值。)