使用 AWK 选择要复制到文本文件的结果/值的脚本

使用 AWK 选择要复制到文本文件的结果/值的脚本

有人可以帮我完成 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,用测试和语句包装echoand命令:printfif

    ...
    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 ''选项来完成的。readread

最后,您还需要能够处理返回的多个编解码器,因为这似乎很常见,至少对于我测试的文件来说是这样。例如:

$ 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

相关内容