我正在尝试让 FFmpeg 遍历我运行命令的路径下的所有文件和子文件夹,将任何 MKV 文件转换为 MP4 容器。
我已经运行了下面的命令,但是创建的新 MP4 文件除了新的 MP4 扩展名外,仍然具有 MKV 扩展名。
find ./ -iname '*.avi' -o -iname '*.mkv' -exec bash -c 'ffmpeg -i "{}" -vcodec copy -acodec copy "{}".mp4' \;
例如 :
abcd.mkv (original file)
abcd.mkv.mp4 (new file)
如何调整命令以摆脱原始文件扩展名?
答案1
如果你希望保持你的现存的语法(这对你来说可能很有意义)以下轻微的修改应该保留这个含义,而还完成所需的文件名重命名:
find -iname '*.avi' -o -iname '*.mkv' -exec \
bash -c 'ffmpeg -i "{}" -codec copy \
$(echo "{}" | sed -r 's/.{3}$/mp4/')' \;
请注意,我也简化使用 FFmpeg 的语法copy
...
答案2
有一种更简单的方法可以做到这一点,即将ffmpeg
命令放入bash
脚本并使用bash
工具。而不是:
find ./ -iname '*.avi' -o -iname '*.mkv' -exec \
bash -c 'ffmpeg -i "{}" -vcodec copy -acodec copy "{}".mp4' \;
使用find
和xargs
传递文件名列表(包括带空格的文件名),例如$HOME/bin/fixthem
(读取man find;man xargs
):
find . -type f -iname '*.avi' -o -iname '*.mkv' -print0 |\
xargs -0 -r $HOME/bin/fixthem
类似于$HOME/bin/fixthem
(和chmod +x
'd):
注意:未经测试,但可以运行/usr/bin/shellcheck
。
#!/bin/bash
# convert the *.mkv and *.avi files to .mp4
# determine my name
me=$0
me=${me##*/}
# -h or --help
help () {
cat >&2 <<EOF
${me} [-h|--help] [-d|--debug] [-v|--verbose] [-n|--noaction] file file ...
${me}: -h or --help This message
${me}: -d or --debug List the commands we execute, as we process
${me}: the files.
${me}: -v or --verbose List the filenames we process, as we process
${me}: the files.
${me}: -n or --noaction Don't actually execute the commands. Used in
${me}: connection with --debug for testing.
${me}: -r or --remove Remove the input file unless --debug is set.
EOF
exit 2
}
declare -i debug=0 verbose=0 noaction=0 remove=0
# from /usr/share/doc/util-linux/examples/getopt-parse.bash 2015-Sep-06
TEMP=$(getopt -o dhvnr --long debug,help,verbose,noaction,remove \
-n 'fixthem.bash' -- "$@")
if [[ $? != 0 ]] ; then echo "${me} --help for help." >&2 ; exit 1 ; fi
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
while true ; do
case "$1" in
-d|--debug) debug=1; shift;;
-h|--help) help; shift;;
-v|--verbose) verbose=1; shift;;
-n|--noaction) noaction=1; shift;;
-r|--remove) remove=1; shift;;
--) shift; break;;
*) echo "Internal error! ${me} --help for help";exit 1;;
esac
done
# actual processing begins here
while [[ $# -gt 0 ]] ; do
infile="$1"
shift
nameonly="${infile%.*}"
outfile="${nameonly}.mp4"
[[ "$verbose" -ne 0 ]] && echo "$infile -> $outfile" >&2
command="ffmpeg -i \"$infile\" -vcodec copy -acodec copy \"$outfile\""
[[ "$debug" -ne 0 ]] && echo "command" >&2
[[ "$noaction" -ne 0 ]] || eval "$command"
if [[ "$remove" -ne 0 ]] ; then
v=""
[[ "$verbose " -ne 0 ]] && v="-v"
if [[ "$debug" -ne 0 ]] ; then
echo "rm \"$infile\"" >&2
else
rm $v "$infile"
fi
fi
done
exit 0
答案3
另一种方法是,如果您的文件名没有空格,换行符或其他奇怪的字符:
for file in $(find -iname '*.avi' -o -iname '*.mkv')
do
ffmpeg -i $file -codec copy ${file%%.*}.mp4
done
这将删除最后一个点 (.) 后的所有扩展名,然后添加.mp4
。如果文件名中可能存在空格,请改用以下命令:
find . -iname '*.avi' -o -iname '*.mkv' -print0 | while read -d $'\0' file;
do
ffmpeg -nostdin -i "$file" -codec copy ${file%%.*}.mp4
done