我写这个是为了帮助我批量编码一些视频:
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` \;
Run Code Online (Sandbox Code Playgroud)
然而,它失败了——它需要每个 700-900MB 的文件并用 36KB 的文件替换它。
值得注意的是,HandBrake 能够读取和打印视频文件的属性;只有当它继续对其进行编码时,它才会因为文件无效而失败。所以看起来覆盖发生在它到达......
-o `echo {} | tr mkv m4v`
Run Code Online (Sandbox Code Playgroud)
...但我不确定为什么应该这样做。我只是想将输出文件名从whatever.mkv 更改为whatever.m4v
文件名没有空格。例如,它们的形式12-the-revenge.mkv
为 。
反引号表达式:(echo {} | tr mkv m4v
这不是您想要的,出于各种原因;见下文)在解析 find 命令时展开一次。如果您使用的是bash
,它通常会扩展为{}
:
$ echo {} | tr mkv m4v
{}
Run Code Online (Sandbox Code Playgroud)
而且,事实上,这发生在我机器上安装的每个 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 {} \;
Run Code Online (Sandbox Code Playgroud)
简而言之,HandBrakeCLI
为输入和输出提供了相同的文件,我认为这就是您所看到的,尽管您对症状的描述不是很准确。
不幸的是,没有简单的方法可以找到在操作中执行您希望它执行的扩展名替换-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
Run Code Online (Sandbox Code Playgroud)
然后用 find 调用它:
find /path/to/directory -name '*.mkv' -exec /path/to/mkvtom4v.sh {} +
Run Code Online (Sandbox Code Playgroud)
一些注意事项:
不要使用反引号 (
); 它们已被弃用多年。使用some command
$(some command)
,它更具可读性并允许嵌套。
tr
绝对不是你想要的。它逐字翻译。例如,tr aeiou AEIOU
将使所有元音变为 UppErcAsE。tr mkv m4v
将每个更改k
为 a 4
。有关更多详细信息,请参阅man tr
(或info tr
)。
"${file/%.mkv/.m4v}"
是 bash 特有的搜索和替换语法。在这种情况下,它的意思是:“取 , 的值$file
,如果它以.mkv
(%
在此上下文中表示“以”结尾),则将其替换为.m4v
“。还有很多其他的 bashisms 用于编辑变量的值。man bash
并搜索“参数扩展”。
使用 GNU find,您可以{} +
在-exec
命令的末尾使用(而不是\;
)。它将被尽可能多的找到的文件名替换。有关info find
更多详细信息,请参阅。