即时问题:Shell 引用与 Python 引用
在 shell 中未引用的上下文中,\; 防止 ; 被解析为命令分隔符。但是,当从没有shell=True 的 Python 调用带有参数列表的命令时, 没有 shell 来解释和删除该反斜杠,并且额外的引用使得行为等同于在 shell 中传递 '\;' - - 表示传递给 find 的参数不是它所期望的确切字符串 ;,并且您会收到语法错误。
但是,原始代码在其他方面也是不正确的(以影响安全的方式!);应使用以下解决方案之一。
最小的变化:安全地使用find
就个人而言,如果您愿意在输出名称上稍微灵活一点(即testmkv-27311.mkv.avi),我会这样写:
subprocess.call([
'find', '/',
'-name', 'testmkv-27311.mkv',
'-exec',
'ffmpeg',
'-i', '{}',
'-vcodec', 'copy',
'-acodec', 'copy',
'-codec:v', 'libx264',
'-profile:v', 'high',
'-preset', 'slow',
'-b:v', '500k',
'-maxrate', '500k',
'-bufsize', '1000k',
'-vf', 'scale=-1:480',
'-threads', '0',
'-codec:a', 'libfdk_aac',
'-b:a', '128k',
'{}.mp4',
';',
])
值得注意的是:
- 我们不调用外壳程序,从而避免外壳程序注入式安全漏洞。
- 引用完全使用 Python 语法完成 - 不使用或不需要嵌套的 shell 引用。
- POSIX 不要求
find -exec 支持 {} 作为子字符串(而不是完整的参数)。因此,这个答案(就像问题中的代码一样)不像预期的那样可移植。
替代方法:本地 Python 文件搜索
也就是说,使用 find 并没有真正令人信服的理由,当 Python 标准库可以为您进行搜索时,并且将使您自己更新名称变得容易:
def convert(filename_in, filename_out):
subprocess.call([
'ffmpeg',
'-i', filename_in,
'-vcodec', 'copy',
'-acodec', 'copy',
'-codec:v', 'libx264',
'-profile:v', 'high',
'-preset', 'slow',
'-b:v', '500k',
'-maxrate', '500k',
'-bufsize', '1000k',
'-vf', 'scale=-1:480',
'-threads', '0',
'-codec:a', 'libfdk_aac',
'-b:a', '128k',
filename_out,
])
target = 'testmkv-27311.mkv'
for root, dir, files in os.walk('/'):
if target in files:
filename_in = os.path.join(dir, target)
filename_out = filename_in[:-3]+'.mp4'
convert(filename_in, filename_out)
备用安全方法:硬编码脚本迭代参数
作为另一种安全地执行此操作的方法(不让find 修改或从名称生成代码,这允许这些名称被 shell 解析为代码),你可以让你的 bash脚本迭代其参数:
bash_script=r'''
for filename; do
ffmpeg -i "$filename" \
-vcodec copy \
-acodec copy \
-codec:v libx264 \
-profile:v high \
-preset slow \
-b:v 500k \
-maxrate 500k \
-bufsize 1000k \
-vf scale=-1:480 \
-threads 0 \
-codec:a libfdk_aac \
-b:a 128k \
"${filename%.mkv}.mp4}"
done
'''
subprocess.call([
'find', '/',
'-name', 'testmkv-27311.mkv',
'-exec', 'bash', '-c', bash_script, '_',
'{}', '+'])
在这种情况下,_ 是脚本中$0 的占位符;后续参数(find 找到的文件名)在 $1、$2 等中传递;和for filename; do(默认为for filename in "$@"; do)将遍历它们。
使用-exec ... {} + 将尽可能多的文件名传递给每个命令,而不是每个文件名调用一次命令;这减少了调用的 shell 数量,从而提高了性能。