1

Я хочу использовать команду find, чтобы найти все файлы с определенным расширением (avi), где такого файла с другим расширением (mp4) еще не существует.

с учетом этих файлов:

a.avi
a.mp4
c.avi

это должно привести только к тому, что файл c.avi будет найден.

4 ответа4

1

Будет ли это делать?

find . -name '*.avi' -type f -execdir bash -c 'f=("${0%avi}"*);((${#f[@]}==1))' {} \; -print

Я постараюсь объяснить, как это работает.

Ну, на самом деле это действительно просто: найти все файлы в текущем каталоге, чтобы имя имело расширение avi . Для каждого из них, скажем, это файл, который X.avi выполняет в каталоге, где находится (execdir) команда:

bash -c 'f=("${0%avi}"*);((${#f[@]}==1))' {}

где {} заменяется именем файла, в нашем тестовом примере X.avi . Так что это как

bash -c 'f=("${0%avi}"*);((${#f[@]}==1))' X.avi

На этом этапе позвольте мне подчеркнуть, что это на 100% безопасно в отношении пробелов и других забавных символов в именах файлов! Теперь вы видите фрагмент, который будет выполнять bash? Я имею в виду этот фрагмент

f=("${0%avi}"*);((${#f[@]}==1))

Это будет выполнено с 0-м позиционным параметром, установленным в наше имя файла; в нашем тестовом примере это X.avi .

Часть "${0%avi}" расширяется до имени файла, с удаленным конечным avi (кстати, это имя файла гарантированно будет иметь это расширение в этой точке), а часть "${0%avi}"* будет расширяться ко всем файлам в текущем каталоге (помните, тот, который содержит файл), которые имеют расширение .avi . В нашем тестовом примере это выглядит так:

X.*

Затем мы строим массив f из них и, наконец, успешно завершаем этот процесс bash если f содержит только один элемент (так что, весьма вероятно, f содержит только X.avi в нашем тестовом примере), и в противном случае происходит сбой. Если это было успешно, мы печатаем имя файла.

1

Решение с 2 exec()s:

find . -name '*.avi' -print | \
  perl -n -e '$x = $_; $x =~ s/\.avi\s*$/.mp4/; (-e $x) || print'

шаги:

  1. это те дроиды, которых вы ищете
  2. для каждой входной строки / файла проверьте соответствующий файл .mp4 и выведите имя файла .avi, если оно не найдено
    1. скопировать строку ввода в $ x
    2. замените .mp4 вместо .avi в $ x (но только в конце строки)
    3. если соответствующий файл .mp4 существует, мы закончили с этой строкой, в противном случае выведите строку ввода
0

Другой вариант: найти все файлы .mp4 и .avi , затем исключить из списка все файлы, для которых существуют оба расширения, а затем удалить оставшиеся файлы .mp4:

find . -name '*.avi' -or -name '*.mp4' |
rev | sort -t. -k2 -k1,1 | uniq -s3 -u | grep -v '^4pm' | rev | sort

(Предполагается, что ни у одного файла нет новой строки в названии.)

-1

Решение с 3 exec()s:

find . -name '*.avi' -print | \
  sed 's/\(.*\)\.avi$/if [ ! -f "\1.mp4" ]; then echo "\1.avi"; fi/' | \
  sh

шаги:

  1. это те дроиды, которых вы ищете
  2. удалить расширение файла .avi; написать поток команд sh, который проверяет соответствующий файл .mp4 и выводит имя файла .avi, если не найден
  3. оцените команды sh

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .