为什么这一行会破坏文件?

为什么这一行会破坏文件?

我写这个是为了帮助我对一些视频进行批量编码:

find -name '*.mkv' -exec HandBrakeCLI -Z "Android Tablet" \
  -c "1-3" -m -O --x264-tune film -E copy --decomb -s 1 -i {} \
  -o `echo {} | tr mkv m4v` \;

然而,它会灾难性地失败——它将每个 700-900MB 的文件替换为 36KB 的文件。

值得注意的是,HandBrake 能够读取和打印视频文件的属性;只有当它继续对其进行编码时,才会因为文件无效而失败。所以看来覆盖发生在它到达......

-o `echo {} | tr mkv m4v`

...但我不知道为什么会这样。我只是想将输出文件名从whatever.mkv 更改为whatever.m4v

文件名没有空格。12-the-revenge.mkv例如,它们的形式为。

答案1

反引号表达式:(echo {} | tr mkv m4v不是出于各种原因,你想要什么;见下文)在解析 find 命令时展开一次。如果您使用bash,它通常会扩展为{}

$ echo {} | tr mkv m4v
{}

事实上,这种情况发生在我机器上安装的每个 shell 上,除了fish,它输出一个空行。假设您没有使用fish,那么实际看到的参数find将是:

find -name '*.mkv' -exec HandBrakeCLI -Z "Android Tablet" \
-c "1-3" -m -O --x264-tune film -E copy --decomb -s 1 -i {} \
-o {} \;

简而言之,HandBrakeCLI输入和输出都被给予相同的文件,我认为这就是您所看到的,尽管您对症状的描述不是很准确。

不幸的是,没有简单的方法可以让 find 来执行您希望它在操作中执行的扩展替换-exec。您可以通过将命令传递给 来完成此操作bash -c,但这将涉及命令行上额外的、令人困惑的引用。一个更清晰、更易读的解决方案是创建一个 shell 脚本文件,它可以迭代它的参数:

文件:(mkvtom4v.sh请确保chmod a+x mkvtom4v.sh

#!/bin/bash
for file in "$@"; do
  HandBrakeCLI -Z "Android Tablet" -c "1-3" -m -O \
               --x264-tune film -E copy --decomb -s 1 \
               -i "$file" -o "${file/%.mkv/.m4v}"
done

然后用 find 调用它:

find /path/to/directory -name '*.mkv' -exec /path/to/mkvtom4v.sh {} +

一些注意事项:

  1. 不要使用反引号 ( some command);它们已被弃用多年。使用$(some command),它更具可读性并且允许嵌套。

  2. tr绝对不是你想要的。它逐个字符地进行翻译。例如,tr aeiou AEIOU将使所有元音UppErcAsE。tr mkv m4v将把 every 改为ka 4。请参阅man tr(或info tr) 了解更多详细信息。

  3. "${file/%.mkv/.m4v}"是 bash 特殊的搜索和替换语法。在这种情况下,它的意思是:“取 的值$file,如果它以.mkv%在此上下文中表示“以”结尾)结尾,则将其替换为.m4v“。还有很多其他的方法可以编辑变量的值。man bash并搜索“参数扩展”。

  4. 使用 GNU find,您可以{} +在命令末尾使用-exec(而不是\;)。它将被尽可能多的找到的文件名替换。请参阅info find了解更多详情。

相关内容