BASH: Filter list of files by return value of another command

无人久伴 提交于 2021-01-27 20:04:31

问题


I have series of directories with (mostly) video files in them, say

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

I create a variable (video_dir) with the directory names (based on other parameters) and use that with find to generate the basic list. I then filter based on another variable (video_type) for file types (because there is sometimes non-video files in the dirs) piping it through egrep. Then I shuffle the list around and save it out to a file. That file is later used by mplayer to slideshow through the list. I currently use the following command to accomplish that. I'm sure it's a horrible way to do it, but it works for me and it's quite fast even on big directories.

video_dir="/test1 /test2"
video_types=".mpg$|.avi$|.mpeg$"

find ${video_dir} -type f    |
  egrep -i "${video_types}"  |
  shuf > "$TEMP_OUT"

I now would like to add the ability to filter out files based on the resolution height of the video file. I can get that from.

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

Which just returns a number. I have tried using the -exec functionality of find to run that command on each file.

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

but that just returns the list of heights, not the filenames and I can't figure out how to reject ones based on a comparison, like <480. I could do a for next loop but that seems like a bad (slow) idea.

Using info from @mark-setchell I modified it to,

video_dir="test1"

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

Which works.


回答1:


You can replace your egrep with the following so you are still inside the find command (-iname is case insensitive and -o represents a logical OR):

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

The NEXT_BIT can then -exec bash and exit with status 0 or 1 depending on whether you want the current file included or excluded. So it will look like this:

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

So, taking note of @tripleee advice in comments about superfluous exit statements, I get this:

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



回答2:


Here is my solution.

#!/bin/bash

shopt -s extglob

video_dir=(/test1 /test2)
video_types=(*.@(mpg|avi|mpeg|mp4))

while read -r file; do
  if [[ $file == ${video_types[@]} ]]; then
    h=$(mediainfo --Output="Video;%Height%"  "$file")
    (( h >= 480 )) && echo "$file"
  fi
done < <(find "${video_dir[@]}" -type f)

That really needs extglob enabled otherwise the syntax *.@(...) will throw some errors. This solution you can process everything inside the while read loop.



来源:https://stackoverflow.com/questions/59699174/bash-filter-list-of-files-by-return-value-of-another-command

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!