Как я могу найти все подкаталоги в каталоге (только на 1 уровень ниже), которые не содержат данный файл?
2 ответа
Есть несколько способов сделать это с помощью find.
Подход 1 менее хакерский и будет хорошо работать со странными именами каталогов (например, имена каталогов, содержащие новые строки), но подход 2 должен быть более быстрым, если существует много каталогов.
Подход 1
команда
find DIR -type d -mindepth 1 -maxdepth 1 -not -exec test -f {}/FILENAME \; \
-print | sort
Как это устроено
find DIR -type d -mindepth 1 -maxdepth 1
находит все каталоги (-type d
) в DIR с глубиной 1.-not -exec test -f {}/FILENAME \;
Истинно, если и только если файл с именем FILENAME не может быть найден в текущем обработанном каталоге ({}
).-print
выведет нужные имена каталогов.При желании
sort
отсортирует вывод по алфавиту.
Подход 2
команда
( find DIR -type f -mindepth 2 -maxdepth 2 -name FILENAME -printf "%h\n" ; \
find DIR -type d -mindepth 1 -maxdepth 1 ) | sort | uniq -u
Как это устроено
find DIR -type f -mindepth 2 -maxdepth 2 -name FILENAME
находит все файлы (-type f
) с именем FILENAME в подкаталогах DIR (файлы каталогов глубины 1 имеют глубину 2).-print "%h\n"
вывести имена каталогов, содержащих файлы с именем FILENAME, с последующим переводом строки.find DIR -type d -mindepth 1 -maxdepth 1
перечислить все каталоги (-type d
) в DIR с глубиной 1.sort
сортирует выходные данные в алфавитном порядке (выходные данные должны быть отсортированы при передаче вuniq
).uniq -u
печатает только уникальные строки.Каждый подкаталог DIR перечисляется по крайней мере один раз, но те, которые содержат файл с именем FILENAME, появляются в списке дважды.
uniq -u
исключает последний вид.
Прямо в сценарии оболочки:
for i in DIRECTORY/*/; do [ -f "$i/FILENAME" ] || basename "$i"; done
for i in DIRECTORY/*/
использует расширение оболочки, чтобы безопасно (не должно быть проблем со странными именами каталогов) выдавать все подкаталоги определенного каталога. Обратите внимание на косую черту, чтобы дать только каталоги.[ -f "$i/FILENAME" ]
возвращает true, если файл с именемFILENAME
существует в каталоге на этой итерации.||
Оператор запускает следующую команду, если первая вернула false, т.е. только если файл не существует в каталоге.basename "$i"
печатает имя каталога (если имя файла там не найдено). Если вам нужен полный путь, а не только имя каталога, заменитеbasename
наecho
илиreadlink -f
или что-то еще в предпочтении.
Если вы также хотите включить скрытые каталоги, запустите (в Bash)
shopt -s dotglob
перед командой.