仅使用一行 xargs 将 mkv 文件批量转换为 mp4 文件

Rav*_*euk 4 command-line video ffmpeg batch-rename

这个答案使用 for 循环将一批 mp4 转换为 mp3。

我想只用一行代码实现类似的目标。经过一番尝试和错误,我想出了以下代码。

ls | grep .mkv | xargs -I {} ffmpeg -i {} -codec copy {}.mp4
Run Code Online (Sandbox Code Playgroud)

但是,输出文件名现在是video.mkv.mp4.

我知道我可以简单地再写一行来更改扩展名

ls | grep .mkv.mp4 | sed -e "p;s/\.mkv.mp4$/\.mp4/" | xargs -n2 mv
Run Code Online (Sandbox Code Playgroud)

我想知道是否有一种单线解决方案。

ste*_*ver 5

没有理由(除了可读性之外)您不能将 shell 循环放在一行代码中 - 无论您使用 shell 循环还是xargs -I{}最终都会对ffmpeg每个文件调用一次。

无论如何,我会避免ls | grep- 相反,使用 shell glob。因此,作为一句话(在任何类似 POSIX 的 shell 中,例如 bash):

for f in ./*.mkv; do ffmpeg -i "$f" -codec copy "${f%.mkv}.mp4"; done
Run Code Online (Sandbox Code Playgroud)

如果您决定使用xargs,那么一种选择是切换到 Z-shell,您可以在其中使用glob 限定符生成每个匹配文件名的,然后根据需要添加输入和输出扩展名。例如:

print -rNC1 ./*.mkv(.N:r) | xargs -r0 -I{} ffmpeg -i {}.mkv -codec copy {}.mp4    # NB zsh not bash
Run Code Online (Sandbox Code Playgroud)

print请注意,我将and -切换为空分隔符xargs,这允许您处理任何合法文件名,包括包含换行符的文件名。

另一种选择是从xargsto切换GNU parallel,它具有用于替换字符串的附加选项,包括{.}替换不带扩展名的文件名(相当于循环版本${f%.mkv}或 Z-shell 的:r修饰符):

printf '%s\0' ./*.mkv | parallel -q0 ffmpeg -i {} -codec copy '{.}.mp4'    # any shell, including bash
Run Code Online (Sandbox Code Playgroud)

  • @RavenCheuk 这个答案 https://unix.stackexchange.com/a/128987/111521 很好地解释了为什么你不应该使用`ls | grep`。 (3认同)
  • ...[为什么你不应该解析 ls(1) 的输出](https://mywiki.wooledge.org/ParsingLs) 上有一个稍微更具可读性的阐述。在任何情况下,请注意 `grep .mkv` 将匹配 *任何单个字符* 后跟 `mkv`,*行中的任何位置* - 您至少希望 `grep '\.mkv$'` 匹配点扩展名具体来说。 (2认同)