Используйте GNU find, чтобы показать только листовые каталоги - PullRequest
38 голосов
/ 24 ноября 2010

Я пытаюсь использовать GNU find для поиска только каталогов, которые не содержат других каталогов, но могут содержать или не содержать обычные файлы.

Мое лучшее предположение до сих пор было:

find dir -type d \( -not -exec ls -dA ';' \)

но я получаю длинный список "."

Спасибо!

Ответы [ 6 ]

72 голосов
/ 24 ноября 2010

Вы можете использовать -links, если ваша файловая система совместима с POSIX (т. Е. Каталог содержит ссылку для каждого подкаталога в нем, ссылку от своего родителя и ссылку на себя, таким образом, счетчик из 2 ссылок, если у него нет подкаталогов).

Следующая команда должна делать то, что вы хотите:

find dir -type d -links 2

Однако, похоже, она не работает в Mac OS X (как упомянул @Piotr).Вот еще одна версия, которая медленнее, но работает на Mac OS X. Она основана на его версии с исправлением для обработки пробелов в именах каталогов:

find . -type d -exec sh -c '(ls -p "{}"|grep />/dev/null)||echo "{}"' \;
5 голосов
/ 27 мая 2018

Я только что нашел другое решение, которое работает как на Linux, так и на macOS (без find -exec)!

Включает sort (дважды) и awk:

find dir -type d | sort -r | awk 'a!~"^"$0{a=$0;print}' | sort

Пояснение:

  1. сортировка вывода find в обратном порядке

    • теперь у вас появляются подкаталоги, потом их родители
  2. используйте awk для пропуска строк, если текущая строка является префиксом предыдущей строки

    • (эта команда из ответа здесь )
    • теперь вы удалили «все родительские каталоги» (у вас остались родительские каталоги)
  3. sort их (так выглядит нормальный find вывод)
  4. Вуаля! Быстрый и портативный.
2 голосов
/ 28 апреля 2011

@ Сильвианское решение не работает для меня на Mac OS X по какой-то неясной причине. Так что я придумал немного более прямое решение. Надеюсь, это кому-нибудь поможет:

find . -type d  -print0 | xargs -0 -IXXX sh -c '(ls -p XXX | grep / >/dev/null) || echo XXX' ;

Пояснение:

  • ls -p заканчивает каталоги знаком '/'
  • поэтому (ls -p XXX | grep / >/dev/null) возвращает 0, если нет каталогов
  • -print0 && -0 - заставить xargs обрабатывать пробелы в именах каталогов
0 голосов
/ 23 сентября 2018

У меня есть несколько странно названных файлов в моих деревьях каталогов, которые путают awk, как в ответе @AhmetAlpBalkan.Поэтому я выбрал немного другой подход

  p=;
  while read c;
    do 
      l=${#c};
      f=${p:0:$l};
      if [ "$f" != "$c" ]; then 
        echo $c; 
      fi;
      p=$c; 
    done < <(find . -type d | sort -r) 

Как и в решении awk, я выполняю обратную сортировку.Таким образом, если путь к каталогу - это подпуть предыдущего попадания, вы можете легко распознать это.

Здесь p - мое предыдущее совпадение, c - текущее совпадение, l - длинатекущее совпадение, f - это первое l совпадение символов предыдущего совпадения.Я только echo обращаюсь к тем совпадениям, которые не соответствуют началу предыдущего совпадения.

Проблема с предлагаемым решением awk состоит в том, что сопоставление начала строки кажется запутанным, еслиимя пути содержит такие вещи, как + в названии некоторых подкаталогов.Это заставило awk вернуть мне несколько ложных срабатываний.

0 голосов
/ 23 сентября 2015

Вот решение, которое работает на Linux и OS X:

find . -type d -execdir bash -c '[ "$(find {} -mindepth 1 -type d)" ] || echo $PWD/{}' \; 

или

find . -type d -execdir sh -c 'test -z "$(find "{}" -mindepth 1 -type d)" && echo $PWD/{}' \;
0 голосов
/ 04 сентября 2015

А как насчет этого?Это портативное устройство, и оно не зависит от количества ссылок.Обратите внимание, однако, что важно поставить root/folder без трейлинг /.

find root/folder -type d | awk '{ if (length($0)<length(prev) || substr($0,1,length(prev))!=prev) print prev; prev=($0 "/") } END { print prev }'
...