Один из способов сделать это - создать собственную рекурсивную ссылку для чтения
.
function readlinkWithPrint() {
link=`readlink "$@"`
[ -e "$link" ] && echo "$link"
[ -h "$link" ] && readlinkWithPrint "$link"
}
Вот тест:
$ touch file
$ ln -s file symlink1
$ ln -s symlink1 symlink2
$ readlinkWithPrint symlink2
symlink1
file
К сожалению, эта функция довольно проста; параметры, которые вы предоставляете для frist readlink
, не будут распространяться на остальные, плюс вы не можете читать несколько файлов.
Чтобы найти только те конечные каталоги, которые содержат файлы, вы можете объединить ответ на указанный вопросhttps://unix.stackexchange.com/a/203991/330217или аналогичные вопросыhttps://stackoverflow.com/a/4269862/10622916илиhttps://serverfault.com/a/530328с find
's! -empty
find rootdir -type d -links 2 ! -empty
Проверка жестких ссылок с помощью -links 2
должна работать для традиционных файловых систем UNIX. Условие -empty
не является частью стандарта POSIX, но должно быть доступно в большинстве систем Linux.
Согласно комментарию Kamil Maciorowski, традиционная семантика подсчета ссылок для каталогов недействительна для Btrfs. Это подтверждается в https://linux-btrfs.vger.kernel.narkive.com/oAoDX89D/btrfs-st-nlink-for-directories, где также упоминается Mac OS HFS+ как исключение из традиционного поведения. Для этих файловых систем необходим другой метод проверки листовых каталогов.
Вы можете использовать вложенные find
и количество подкаталогов:
find. -type d \
\( -exec sh -c 'find "$1" -mindepth 1 -maxdepth 1 -type d -print0 | grep -cz "^" >/dev/null 2>&1' _ {} \; -o -print \)
Если шаблон подстановки имени файла */
расширяется до чего-то, что не является именем каталога, то текущий каталог не имеет (не -скрытых )подкаталогов.
Сfind
:
find root -type d -exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; ! -empty -print
Обратите внимание, что при этом символическая ссылка на каталог в конечном каталоге будет рассматриваться как каталог, поскольку шаблон будет проходить по символической ссылке.
Предикат -empty
не является стандартным, но часто используется. Без него вы бы сделали что-то похожее на обнаружение подкаталогов:
find root -type d \
-exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; \
-exec sh -c 'set -- "$1"/*; [ -e "$1" ]' sh {} \; -print
Или, более эффективно,
find root -type d -exec sh -c '
dir=$1
set -- "$dir"/*/
[ -d "$1" ] && exit 1
set -- "$dir"/*
[ -e "$1" ]' sh {} \; -print
Или, используя предикат -links
, о котором я забыл,(спасибо Бодо):
find root -type d \
-links 2 \
-exec sh -c 'set -- "$1"/*; [ -e "$1" ]' sh {} \; -print
Сzsh
:
leafdirs=(**/*(ND/Fl2))
Задает массив $leafdirs
со списком соответствующих конечных каталогов.
Вы можете распечатать его по одному в строке с помощью:
printf '%s\n' $leafdirs
Или перебрать их с помощью:
for dir ($leafdirs) something with $dir
Это более или менее перевод ответа @Bodo GNU find
, так что он работает только на традиционных Unix -, таких как файловые системы, такие как ext4
, которые реализуют .
и ..
. ] как фактические записи каталога.
**/
:любой уровень подкаталогов (NF/Fl2)
:квалификатор глоба N
:включить nullglob
для этого одного шара :не сбой, если нет совпадения. Вместо этого получите пустой список D
:включить dotglob
для этого одного шара :просматривать скрытые каталоги и не пропускать скрытые файлы /
:выбирать файлы только каталога типа (например-type d
)F
:выберите полные каталоги :каталоги, содержащие хотя бы одну запись, отличную от .
и..
(подобно GNU find
's! -empty
)l2
:только каталоги с количеством ссылок 2 (например-links 2
). Один для записи каталога в его родительском каталоге и один для .
в нем. Любой подкаталог добавит единицу к счетчику ссылок из-за записи ..
в них.