BASH:按另一个命令的返回值筛选文件列表



我有一系列目录,其中(大部分(包含视频文件,比如

test1
1.mpg
2.avi
3.mpeg
junk.sh
test2
123.avi
432.avi
432.srt
test3
asdf.mpg
qwerty.mpeg

我创建一个带有目录名称(基于其他参数(的变量(video_dir(,并将其与find一起使用以生成基本列表。 然后,我根据另一个变量(video_type(过滤文件类型(因为目录中有时有非视频文件(,将其通过egrep。 然后我打乱列表并将其保存到文件中。 该文件稍后被mplayer用于幻灯片列表。 我目前使用以下命令来完成此操作。 我敢肯定这是一种可怕的方法,但它对我有用,即使在大目录中也非常快。

video_dir="/test1 /test2"
video_types=".mpg$|.avi$|.mpeg$"
find ${video_dir} -type f    |
egrep -i "${video_types}"  |
shuf > "$TEMP_OUT"

我现在想添加根据视频文件的分辨率高度过滤掉文件的功能。 我可以从中得到它。

mediainfo --Output='Video;%Height%' filename

它只返回一个数字。 我尝试使用 find 的 -exec 功能在每个文件上运行该命令。

find ${video_dir} -type f -exec mediainfo --Output='Video;%Height%' {} ;

但这只返回高度列表,而不是文件名,我不知道如何根据比较拒绝它们,例如 <480。 我可以做一个下一个循环,但这似乎是一个糟糕(缓慢(的主意。

使用来自@mark的信息,我将其修改为,

video_dir="test1"
find ${video_dir} -type f   
-exec bash -c 'h=$(mediainfo --Output="Video;%Height%" "$1"); [[ $h -gt 480 ]]' _ {} ; -print

哪个有效。

您可以将egrep替换为以下内容,以便您仍然在find命令中(-iname不区分大小写,-o表示逻辑 OR(:

find test1 test2 -type f                                       
( -iname "*.mpg" -o -iname "*.avi" -o -iname "*.mpeg" ) 
NEXT_BIT

然后,NEXT_BIT可以-exec bash并以状态 0 或 1 退出,具体取决于您是要包含还是排除当前文件。所以它看起来像这样:

-exec bash -c 'H=$(mediainfo -output ... "$1"); [ $H -lt 480 ] && exit 1; exit 0' _ {} ;

因此,注意到评论中关于多余exit陈述@tripleee建议,我得到:

find test1 test2 -type f                                       
( -iname "*.mpg" -o -iname "*.avi" -o -iname "*.mpeg" )  
-exec bash -c 'h=$(mediainfo ...options... "$1"); [ $h -lt 480 ]' _ {} ; -print

这个问答集中在一个特定的案例上,所以接受的答案并不像它可能的那样笼统。

find

如果文件列表来自find,则可以使用其过滤工具,例如-exec

find ${video_dir} -type f 
-exec COMMAND ; 
-print

这里

  • COMMAND 没有用引号括起来 -find读取-exec之后的所有内容,直到一个;
  • find{}展开到当前文件名(包括路径 - 您可能会发现-execdir有帮助,这将cd到文件的目录并将{}替换为叶文件名(
  • COMMAND 的退出代码处理如下:
    • 0 -> 真
    • 非 0 -> 假

请注意,您可以构建更复杂的表达式(例如-not -exec ...(,将"根据优先规则从左到右...... 在省略运算符的情况下假定-and"(每人找到(

xargs

如果文件列表来自其他地方(并且在 stdin(并且在 stdin(上可用(,则可以按如下方式使用xargs(从 如果 xargs 是映射,什么是过滤器?)

ls | xargs -I{} bash -c "COMMAND '{}' && echo '{}'"

这是我的解决方案。

#!/bin/bash
shopt -s nullglob
video_dir=(/test1 /test2)
while IFS= read -rd '' file; do
if [[ $file = *.@(mpg|avi|mpeg|mp4) ]]; then
h=$(mediainfo --Output="Video;%Height%"  "$file")
(( h >= 480 )) && echo "$file"
fi
done < <(find "${video_dir[@]}" -type f -print0)

此解决方案您可以在读取循环中处理所有内容。

最新更新