有人可以帮我完成 bash 脚本的最后一步吗?你帮助我已经走到这一步了。
#!/bin/bash
find . -type f \
-name '*.mp4' -o -name '*.mkv' \
-o -name '*.avi' -o -name '*.mov' |
while read -r file
do
size=$(stat -c %s "$file")
duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file")
codec=$(ffprobe -v error -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 "$file")
ratio=$(bc <<< "scale=2; $size / $duration")
echo "$file: codec=$codec, size=$size, duration=$duration, ratio=$ratio" | tee -a /home/user/Downloads/logfile
printf $ratio | awk '{print $1/1000}'| tee -a /home/user/Downloads/logfile
done
现在所有结果都进入文本文件。但是有没有办法只选择比率大于...的文件?
根据@markp-fuso的要求,我澄清了几点:
请用 $ratio 中的一些示例更新问题
$ratio 中的数字是通过将视频文件大小(以字节为单位)除以视频文件的持续时间(以秒为单位)生成的。这些数字大约为 50k 到 1000k。因此我使用 awk '{print $1/1000}' 将它们带入 50 - 1000 的范围内。
以及您正在考虑使用什么作为截止/阈值;您是否希望根据 a) $ratio 中的值、b) awk 生成的值或 c) numfmt 调用的结果进行过滤?
好点,我想使用 numfmt 调用将字节提高到兆字节。但这已经被削减了。所以可以删除它,我特此这样做了。它会一直工作到产生想要的结果:
./file1.mp4: codec=h264
aac, size=54886926, duration=94.900000, ratio=578365.92
578.366
./file2.mp4: codec=vp9
aac, size=15147100, duration=108.159000, ratio=140044.74
140.045
./file3.mp4: codec=vp9
aac, size=22306731, duration=109.947000, ratio=202886.21
202.886
我将使用它来查找可以编码/缩小的视频/音频文件,因为它们在持续时间内很大。因此可以编码高 $ratio。该值可以在脚本中轻松调整,但会在 200-400 左右。取决于编解码器效率(我刚刚添加了一行来显示所使用的编解码器)。
所以最后我喜欢有一个文本文件,其中只包含那些满足要求的文件,在这种情况下,比设定的比率更大。我将根据经验做出决定。
笔记:如果可能的话,如果无法读取(例如由于损坏)且没有任何价值的文件将被添加到文本文件中,那就太好了。
假设我将比率设置为 200,那么基于上面的 3 个示例,txt 文件应包含以下内容:
./file1.mp4: codec=h264
aac, size=54886926, duration=94.900000, ratio=578365.92
578.366
./file3.mp4: codec=vp9
aac, size=22306731, duration=109.947000, ratio=202886.21
202.886
任何帮助将不胜感激。
干杯
答案1
可能在顶部附近,声明您的截止值:
# We only care about files with ratios GREATER than this:
cutoff=200000
然后在循环底部附近while
,用测试和语句包装echo
and命令:printf
if
...
ratio=$(bc <<< "scale=2; $size / $duration")
rc=$(bc <<< "$ratio > $cutoff")
if [[ "$rc" == "1" ]]
then {
echo "$file: codec=$codec, size=$size, duration=$duration, ratio=$ratio"
awk '{print $1/1000}' <<< "$ratio"
} | tee -a /home/user/Downloads/logfile
fi
done
答案2
在解决您所询问的问题之前,您应该先解决脚本中的各种问题。首先,您的find
命令是错误的,其次,这对于包含换行符的文件名将失败。
您的find
错误是因为您没有对选项进行分组。这意味着您的命令还将查找名称以 eg 结尾的目录.mov
。考虑这个目录:
$ ls -lF
total 4
-rw-r--r-- 1 terdon terdon 0 Mar 18 18:37 'a bad'$'\n''file name.mp4'
drwxr-xr-x 2 terdon terdon 4096 Mar 18 18:38 foo.mov/
其中包含一个文件(其名称包含空格和换行符)和一个目录foo.mov
.您只想处理文件,但您find
还将返回目录:
$ find . -type f -name '*.mp4' -o -name '*.mkv' -o -name '*.avi' -o -name '*.mov'
./foo.mov
./a bad?file name.mp4
您希望-type f
适用于所有条件,为此,您需要将它们分组为一个答案对于您之前提到的问题:
$ find . -type f \( -name '*.mp4' -o -name '*.mkv' -o -name '*.avi' -o -name '*.mov' \)
./a bad?file name.mp4
正如您在上面所看到的,用括号将它们分组(它们需要转义\(
或引用'('
以保护它们免受 shell 的影响)使命令根据需要仅查找文件。下一个问题是换行符。您可以通过告诉find
打印结果以 NULL ( \0
) 字节而不是换行符分隔来解决此问题。 GNU find
(Linux 系统上的默认设置)可以使用 来执行此操作-print0
,对于其他查找实现,您可以使用-printf
。
如果您不处理此类名称,则会出现以下错误:
$ find . -type f \( -name '*.mp4' -o -name '*.mkv' -o -name '*.avi' -o -name '*.mov' \) | while read -r file; do ls -l "$file"; done
ls: cannot access './a bad': No such file or directory
ls: cannot access 'file name.mp4': No such file or directory
正确的方法如下:
$ find . -type f \( -name '*.mp4' -o -name '*.mkv' -o -name '*.avi' -o -name '*.mov' \) -print0 | while read -r -d '' file; do ls -l "$file"; done
-rw-r--r-- 1 terdon terdon 0 Mar 18 18:37 './a bad'$'\n''file name.mp4'
这里IFS=
不是必需的,但使用它是一个很好的做法。看这个答案例如,真正的工作是通过告诉使用 NULL 作为输入分隔符的-d ''
选项来完成的。read
read
最后,您还需要能够处理返回的多个编解码器,因为这似乎很常见,至少对于我测试的文件来说是这样。例如:
$ ffprobe -v error -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 foo.mkv
hevc
ac3
ass
ffprobe
因此,通过命令的输出tr '\n' ','
或其他内容来删除换行符:
$ ffprobe -v error -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 foo.mkv | tr '\n' ','
hevc,ac3,ass,$
(最后一个$
是我的提示,显示在那里表明这里没有尾随换行符。)
现在,综上所述,由于您已经在变量中拥有了比率,因此您所需要的只是简单地if
检查它是否高于您的阈值。我不明白为什么你有两个比率($ratio
和$ratio / 1000
),对我来说,只使用你实际测试的比率似乎更合理,但这是你的决定。这是一个工作脚本:
#!/bin/bash
threshold=$1
if [ -z "$threshold" ]; then
echo "No threshold given, using the default value of 200" >&2
threshold=200
fi
logfile="/home/user/Downloads/logfile"
find . -type f \
\( -name '*.mp4' -o -name '*.mkv' -o \
-name '*.avi' -o -name '*.mov' \) -print0 |
while IFS= read -r -d '' file
do
size=$(stat -c %s "$file")
duration=$(ffprobe -v error -show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 "$file")
codec=$(ffprobe -v error -show_entries stream=codec_name \
-of default=noprint_wrappers=1:nokey=1 "$file" |
tr '\n' ',')
ratio=$(bc <<< "scale=2; $size / $duration")
# Check that a ratio was found, otherwise print an error
if [[ -z "$ratio" ]]; then
echo "No ratio found for '$file'" >&2
else
## Not sure why you want two separate values for ratio but...
ratio2=$(bc <<< "$ratio / 1000")
if [[ $ratio2 -ge $threshold ]]; then
printf '%s: codec=%s size=%s, duration=%s, ratio=%s\n' \
"$file" "$codec" "$size" "$duration" "$ratio" | tee -a "$logfile"
echo "$ratio2" | tee -a "$logfile"
fi
fi
done
现在,您可以使用阈值作为参数来运行它(或者不使用默认值 200 的参数):
script.sh 300
我还对脚本进行了一些其他细微的、主要是美观的更改,并添加了一些基本的错误处理,但它应该做完全相同的事情。输出看起来像:
$ foo.sh 200
./file3.mkv: codec=h264,aac, size=764948534, duration=3488.131000, ratio=219300.40
219
./file7.mkv: codec=h264,aac, size=739550128, duration=3542.852000, ratio=208744.29
208
./file5.mkv: codec=h264,aac, size=688337512, duration=3439.637000, ratio=200119.23
200
./file1.mkv: codec=h264,aac, size=883534591, duration=3701.386000, ratio=238703.71
238
./file4.mkv: codec=h264,aac, size=828112726, duration=3769.898000, ratio=219664.49
219