BASH: фильтрация списка файлов по возвращаемому значению другой команды - PullRequest
2 голосов
/ 12 января 2020

У меня есть серия каталогов с (в основном) видеофайлами, скажем,

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

Я создаю переменную (video_dir) с именами каталогов (на основе других параметров) и использую ее с find для генерации базового c списка. Затем я фильтрую на основе другой переменной (video_type) для типов файлов (потому что иногда в папках есть файлы, не являющиеся видео), пропуская их через egrep. Затем я перемешиваю список и сохраняю его в файл. Этот файл позже используется mplayer для показа слайдов по списку. В настоящее время я использую следующую команду для выполнения sh этого. Я уверен, что это ужасный способ сделать это, но он работает для меня и довольно быстро даже для больших каталогов.

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

, который просто возвращает число. Я пытался использовать функциональность -exe c команды find для запуска этой команды для каждого файла.

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

, но это просто возвращает список высот, а не имен файлов, и я не могу понять, как отклонить те, которые основаны на сравнении, например, <480. Я мог бы сделать для следующего l oop, но это кажется плохой (медленной) идеей. </p>

Используя информацию из @ mark-setchell Я изменил ее на

video_dir="test1"

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

Что работает .

Ответы [ 2 ]

1 голос
/ 12 января 2020

Вы можете заменить egrep на следующее, так что вы все еще находитесь внутри команды find (-iname без учета регистра и -o представляет логическое ИЛИ):

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' _ {} \;

Итак, принимая к сведению советы @tripleee в комментариях о лишних exit утверждениях, я получаю следующее:

find test1 test2 -type f                                       \
    \( -iname "*.mpg" -o -iname "*.avi" -o -iname "*.mpeg" \)  \
    -exec bash -c 'h=$(mediainfo ...options... "$1"); [ $h -lt 480 ]' _ {} \; -print
0 голосов
/ 13 января 2020

Вот мое решение.

#!/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)

Для этого действительно необходим extglob, иначе синтаксис *. @ (...) вызовет некоторые ошибки. В этом решении вы можете обрабатывать все внутри, пока читается l oop.

...