【问题标题】:find: missing argument to `-exec' when using subprocess查找:使用子进程时缺少“-exec”的参数
【发布时间】:2017-12-16 12:28:18
【问题描述】:
"find / -name 'testmkv-27311.mkv' -exec bash -c 'ffmpeg -i testmkv-27311.mkv -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 {}testmkv-27311.mp4' \;"

是我正在传递的命令,它给出了查找错误...已经有一些答案,但他们没有说清楚...请帮我执行这个命令...

【问题讨论】:

  • 您需要将 {} 转义为 \{},就像使用分号 \;
  • 那没用....我试过了
  • 您收到的错误是什么?
  • 请发布您正在使用的实际代码。但请注意,在空格上拆分该字符串不会产生您想要的命令,因为您的 bash -c 命令不会是单个参数,这可能是您问题的根源。要么将其保留为带有shell=True 的字符串,要么将其构建为列表而不是字符串,或者尝试使用shlex.split 而不是天真的字符串split
  • 另外,请不要在cmets中添加信息,而是edit您的问题与更新的信息

标签: python bash python-3.x ffmpeg


【解决方案1】:

即时问题: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 数量,从而提高了性能。

【讨论】:

    猜你喜欢
    • 2011-02-27
    • 2014-04-05
    • 2019-06-22
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多