Ответ 1
find . -type d | sort | awk '$0 !~ last "/" {print last} {last=$0} END {print last}'
Есть ли простой способ перечислять только каталоги в данном каталоге в Linux? Чтобы лучше объяснить, я могу сделать:
find mydir -type d
который дает:
mydir/src
mydir/src/main
mydir/bin
mydir/bin/classes
Вместо этого я хочу:
mydir/src/main
mydir/bin/classes
Я могу сделать это в bash script, который перебирает строки и удаляет предыдущую строку, если следующая строка содержит путь, но мне интересно, есть ли более простой метод, который не использует bash loop.
find . -type d | sort | awk '$0 !~ last "/" {print last} {last=$0} END {print last}'
Если вы хотите только листовые каталоги (каталоги, которые не содержат какой-либо подкаталога), посмотрите этот другой вопрос. Ответ также объясняет это, но вкратце:
find . -type d -links 2
Я не могу думать ни о чем, что сделает это без цикла. Итак, вот несколько циклов:
Отображает каталоги листьев в текущем каталоге независимо от их глубины:
for dir in $(find -depth -type d); do [[ ! $prev =~ $dir ]] && echo "$dir" ; prev="$dir"; done
Эта версия правильно обрабатывает имена каталогов, содержащие пробелы:
saveIFS=$IFS; IFS=$'\n'; for dir in $(find -depth -type d ); do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done; IFS=$saveIFS
Вот версия, использующая предложение Jefromi:
find -depth -type d | while read dir; do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done
Если вы ищете что-то визуальное, tree -d
приятно.
drinks |-- coke | |-- cherry | `-- diet | |-- caffeine-free | `-- cherry |-- juice | `-- orange | `-- homestyle | `-- quart `-- pepsi |-- clear `-- diet
Это все еще цикл, так как он использует команду ветвления в sed
:
find -depth -type d |sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
Основываясь на script в info sed
(uniq work-alike).
Изменить. Здесь вызывается sed
script с комментариями (скопировано из info sed
и изменено):
# copy the pattern space to the hold space
h
# label for branch (goto) command
:b
# on the last line ($) goto the end of
# the script (b with no label), print and exit
$b
# append the next line to the pattern space (it now contains line1\nline2
N
# if the pattern space matches line1 with the last slash and whatever comes after
# it followed by a newline followed by a copy of the part before the last slash
# in other words line2 is different from line one with the last dir removed
# see below for the regex
/^\(.*\)\/.*\n\1$/ {
# Undo the effect of
# the n command by copying the hold space back to the pattern space
g
# branch to label b (so now line2 is playing the role of line1
bb
}
# If the `N' command had added the last line, print and exit
# (if this is the last line then swap the hold space and pattern space
# and goto the end (b without a label)
$ { x; b }
# The lines are different; print the first and go
# back working on the second.
# print up to the first newline of the pattern space
P
# delete up to the first newline in the pattern space, the remainder, if any,
# will become line1, go to the top of the loop
D
Вот что делает регулярное выражение:
/
- запустите шаблон^
- соответствует началу строки\(
- запустить группу захвата (подвыражение обратной ссылки).*
- ноль или более (*) любого символа (.)\)
- группа конечного захвата\/
- косая черта (/) (экранированная с \
).*
- ноль или более любого символа\n
- новая строка\1
- копия обратной ссылки (которая в этом случае является тем, что было между началом строки и последней косой чертой)$
- соответствует концу строки/
- завершить шаблонЯ думаю, вы можете посмотреть все каталоги, а затем перенаправить вывод и использовать xargs для подсчета числовых файлов для каждого подкаталога, когда нет подкаталога (xargs find SUBDIR -type d | wc -l... что-то вроде этого, я не могу проверить прямо сейчас), вы нашли лист.
Это все еще цикл.
Попробуйте использовать один однострочный (протестированный в Linux и OS X):
find . -type d -execdir sh -c 'test -z "$(find "{}" -mindepth 1 -type d)" && echo $PWD/{}' \;