find . -type d
обычно будет показывать
./dir1
./dir1/dir2
./dir3
./dir3/dir4
./dir3/dir4/dir5
...
Я просто хочу
./dir1/dir2
./dir3/dir4/dir5
, без своего родителя ./dir1, ...
Другими словами, я хочу видеть только те каталоги, в которых нет подкаталогов.
Есть идеи?
EDIT: Я обнаружил, что -links 2
это работает в обычной среде Linux, например,
docker run -it --rm ubuntu:bionic find /etc -type d -links 2
работает отлично.
Однако, когда я монтирую каталог (из MacOS или Windows) в контейнер Docker, все меняется, это не работает, вы можете попробовать это:
docker run -it --rm -v /etc:/etc_of_host ubuntu:bionic find /etc_of_host -type d -links 2
решение1
С zsh
:
has_subdirs() () (( $# )) ${1-$REPLY}/*(ND/Y1)
print -rC1 -- **/*(ND/^+has_subdirs)
Или напрямую без посреднической функции:
print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
Это:
fn() some-command
определяетfn
функцию сsome-command
телом, как в оболочке Bourne и большинстве оболочек типа Bourne.() { code } args
анонимная функция, которая запускаетсяcode
сargs
позиционным параметром.(( $# ))
здесь тело этой анонимной функции представляет собой арифметическое выражение, которое разрешается какистинныйесли$#
(количество аргументов) не равно нулю.${param-default}
(из оболочки Bourne) расширяется до ,$param
если параметр установлен илиdefault
нет. Здесь с помощью${1-$REPLY}
мы позволяем нашей функции вызываться напрямую (какhas_subdirs mydir
) или как подстановочной квалификаторной функции, как показано ниже.$dir/*(ND/Y1)
расширяется до файлов типа directory в$dir
(/
), включая скрытые (D
otglob), и не выдает ошибку, если совпадений нет (N
ullglob). Но сY1
, мы останавливаемся на первом совпадении. Таким образом, анонимная функция (и, следовательно,has_subdirs
) вернет true, если каталог содержит хотя бы один подкаталог.print -rC1
печатает свои аргументыr
aw на1
C
столбце^+has_subdirs
ограничивается файлами, для которыхhas_subdirs
функция не (^
) возвращает значение true.
Если вам нужно использовать bash
оболочку, просто сделайте следующее:
zsh << 'EOF'
print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF
Или сохранить результат в массиве bash (требуется bash-4.4+):
readarray -td '' array < <(zsh << 'EOF'
print -rNC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF
)
(используя записи, разделенные NUL, чтобы иметь возможность хранить произвольные пути).
Если у вас нет zsh
, но есть perl
, вы можете сделать:
find . -depth -type d -print0 |
perl -l -0ne 'print if rindex $prev, "$_/", 0; $prev = $_'
Или, если у вас GNU awk
:
find . -depth -type d -print0 |
gawk -v 'RS=\0' 'index(prev, $0"/") != 1; {prev = $0}'
Те, у кого есть find
распечатать каталогисначала глубина(выходит из ветвей, на которых они находятся) и вызывает perl
/ awk
для вывода записей, которые не найдены, а затем /
в начале предыдущей записи.
Опять же, чтобы сохранить файлы в массиве bash 4.4+, вам нужно будет переключиться на записи с разделителями NUL при выводе, переместив after -l
или добавив in :-0
perl
-v 'ORS=\0'
gawk
readarray -td array < <(
find . -depth -type d -print0 |
perl -0lne 'print if rindex $prev, "$_/", 0; $prev = $_'
)
readarray -td array < <(
find . -depth -type d -print0 |
gawk -v 'RS=\0' -v 'ORS=\0' 'index(prev, $0"/") != 1; {prev = $0}'
)
В некоторых системах и файловых системах (например, ext4
в файловых системах на базе Linux) вы можете рассчитывать на то, что каталоги, имеющие подкаталоги, имеют количество ссылок больше 2 ( dir
и dir/.
дополнительную ссылку для каждого dir/subdir/..
).
print -rC1 -- **/*(ND/l-3)
Или используя find
в любой оболочке:
find . -type d -links -3
Это не будет работать, например, в системах btrfs
(которые заменили файловые системы по умолчанию в нескольких дистрибутивах Linux), где количество ссылок на каталоги всегда равно единице, поэтому я бы не рекомендовал это как общее решение.ext4
То же самое касается объединенных файловых систем, как в вашем случае, например, overlayfs
где я нахожу некоторыеобъединеныКоличество ссылок в каталогах равно 1, независимо от того, содержат ли они подкаталоги или нет.
Однако, где бы его ни использовали, он имеет преимущество перед решениями, которые вручную подсчитывают подкаталоги, поскольку вам не нужен доступ на чтение к каталогу (нужен только доступ на поиск к его родительскому каталогу).
решение2
То, что вы ищете:
find . -type d -links 2
Каждый каталог в файловой системе имеет как минимум две жесткие ссылки — сам каталог в родительском каталоге и запись '.'. Каждая запись '..' в подкаталогах добавляет новую жесткую ссылку на каталог. Поэтому вам просто нужно найти каталоги с двумя жесткими ссылками.
Команда, предложенная в другом ответе
find . -type d -links -3
Делает то же самое, но с утверждением «каталоги, на которые имеется менее трех жестких ссылок».
решение3
Самый лучший:
p="";(find . -type d;echo) | while read d; do [[ $p && $d != $p/* ]] && echo $p; p=$d; done
В одну строку
a="";(find . -type d;echo )|while read i; do if [[ (! $i =~ $a) && ("$a" != "") ]]; then echo $a; fi; a=$i;done
Как упомянул автор статьи:
a="";(find . -type d;echo )|while read i; do [[ $i != $a/* && ! -z "$a" ]] && echo $a; a=$i;done
решение4
Читайте man find
. -mindepth
Вариант — то, что вам нужно.