Найти список каталогов на один уровень от соответствующего каталога

13

Я пытаюсь получить список каталогов, которые содержатся в определенной папке.

Учитывая эти примеры папок:

foo/bar/test
foo/bar/test/css
foo/bar/wp-content/plugins/XYZ
foo/bar/wp-content/plugins/XYZ/js
foo/bar/wp-content/plugins/XYZ/css
baz/wp-content/plugins/ABC
baz/wp-content/plugins/ABC/inc
baz/wp-content/plugins/ABC/inc/lib
baz/wp-content/plugins/DEF
bat/bar/foo/blog/wp-content/plugins/GHI

Я хотел бы команду, которая вернет:

XYZ
ABC
DEF
GHI

По сути, я ищу папки, которые находятся внутри wp-content / plugins /

Использование findсделало меня ближе всего, но я не могу использовать -maxdepth, потому что папка находится далеко от того места, где я ищу.

Выполнение следующего возвращает все дочерние каталоги рекурсивно.

find -type d -path *wp-content/plugins/*

foo/bar/wp-content/plugins/XYZ
foo/bar/wp-content/plugins/XYZ/js
foo/bar/wp-content/plugins/XYZ/css
baz/wp-content/plugins/ABC
baz/wp-content/plugins/ABC/inc
baz/wp-content/plugins/ABC/inc/lib
baz/wp-content/plugins/DEF
bat/bar/foo/blog/wp-content/plugins/GHI
Ник
источник

Ответы:

14

Просто добавьте -pruneтак, чтобы найденные каталоги не спускались в:

find . -type d -path '*/wp-content/plugins/*' -prune -print

Вы должны процитировать это, так *wp-content/plugins/*как это также оболочка.

Если вы хотите только имена каталогов , в отличие от их полного пути, с GNU find, вы можете заменить -printс -printf '%f\n'или предполагая , что пути к файлам не содержат символы новой строки, конвейеру вывод выше команды на awk -F / '{print $NF}'или sed 's|.*/||'(также при условии , что пути к файлам содержат только действительные символы).

С zsh:

printf '%s\n' **/wp-content/plugins/*(D/:t)

**/является любым уровнем подкаталогов (функция Возникнув в zshв начале ночных рубашек, и теперь в большинстве других оболочек нравится ksh93, tcsh, fish, bash, yashхотя в целом в рамках какого - то варианта), (/)чтобы выбрать только файлы типа каталога , Dвключить скрытые (точку) из них, :tв получить хвост (имя файла).

Стефан Шазелас
источник
Эта находка преобразует строки в dirs в a ?. Для bash (как отмечено в вопросе) это работает .
Исаак
@sorontar, findреализации, которые выводят ?или любой замещающий символ, или любую форму экранирования для непечатаемых символов, делают это только тогда, когда вывод идет в терминал, а не когда он идет в канал, как здесь.
Стефан
Первая (и единственная не zsh) команда, которая есть в вашем ответе, не имеет каналов, это та, которую я тестировал, и которая изменяет переводы строки. Когда вы передали по каналу, как вы сказали: «при условии, что пути к файлам не содержат символов новой строки», вы уже признали, что он также не работает с символами новой строки.
Исаак
@sorontar, извините, я думал, что вы комментируете вторую часть, чтобы сказать, что она будет работать нормально, так как символы новой строки будут экранированы, поэтому не нарушит предположение, что мы делаем для sed/ awkвсе файлы в строке. Теперь можно утверждать, что получение ABC?BCDили ABC\nBCDдля интерактивного / терминального вывода было бы более полезным, чем получение двух ABCи BCDстрок для вызываемого каталога $'ABC\nBCD. Это на уровне представления пользователя, мы могли бы также указать, что для файла, который называется $'AB\bC', когда вывод идет на терминал, вы увидите ACбез такой ?замены ...
Стефан
Поиск с помощью -prune и использование printf для изменения формата вывода дали мне именно то, что я просил. Perfect - спасибо!
Ник
4

Bash-чески:

shopt -s globstar
printf "%s\n" **/wp-content/plugins/*

печатает:

bat/bar/foo/blog/wp-content/plugins/GHI
baz/wp-content/plugins/ABC
baz/wp-content/plugins/DEF
foo/bar/wp-content/plugins/XYZ

или

shopt -s globstar
for d in **/wp-content/plugins/*; do printf "%s\n" ${d##*/}; done

печатает:

GHI
ABC
DEF
XYZ
Джефф Шаллер
источник
Нет, это будет печатать файлы внутри каталогов. Пожалуйста, прочитайте мой ответ.
Исаак
Хорошая точка зрения; Спасибо! Я воссоздал их структуру каталогов, но не поместил в них тестовые файлы.
Джефф Шаллер
4

Вы могли бы иметь findрекурсию, вроде:

find / -type d -path *wp-content/plugins -exec find {} -maxdepth 1 -mindepth 1 -type d \;
DopeGhoti
источник
3

Для Bash: Simple (работает с файлами / директориями с пробелами и символами новой строки ):

shopt -s globstar                    # allow ** for several dirs.
a=(**/wp-content/plugins/*/)         # capture only wanted dirs.
a=("${a[@]%/}")                      # remove trailing slash (dirs) `/`.
printf '%s\n' "${a[@]##*/}"          # print only the last dir name.

Будет печать:

GHI
ABC
DEF
XYZ

Даже если есть файлы, созданные.

Исаак
источник
Обратите внимание, что он не заглядывает внутрь скрытых директорий, пропускает скрытые файлы (см. dotglobОпцию, если это нежелательно) и включает символические ссылки на каталоги в дополнение к каталогам ( **также будет проходить символические ссылки в bash 4.2 и более ранних версиях).
Стефан
@ StéphaneChazelas скрытые файлы? Вопрос не требует ни одного, только dirs. Да, скрытые каталоги могут быть выбраны независимо (как функция, если это необходимо). И да, символические ссылки не являются поперечными в текущей версии bash.
Исаак
1
пожалуйста, не расценивайте мои комментарии как агрессию. Я проголосовал за ваш ответ и добавил примечание внизу, чтобы указать на некоторые факты, которые не обязательно очевидны. findне исключает ни одного файла. globs исключает точечные файлы, стоит отметить разницу между ними.
Стефан
3

Команда treeпредназначена именно для этой цели. Глубину можно контролировать с помощью -Lфлага. Вот пример на местном сайте Wordpress, который я поддерживаю:

$ tree -L 1 wp-content/
wp-content/
├── index.php
├── plugins
└── themes

2 directories, 1 file

$ tree -L 2 wp-content/
wp-content/
├── index.php
├── plugins
   ├── akismet
   ├── contact-form-7
   ├── index.php
   └── wordpress-seo
└── themes
    ├── index.php
    ├── twentyfifteen
    └── twentysixteen

11 directories, 3 files

$ tree -L 1 wp-content/plugins/
wp-content/plugins/
├── akismet
├── contact-form-7
├── index.php
└── wordpress-seo

5 directories, 1 file
dotancohen
источник
1

Основываясь на ответе DopeGhoti, как насчет цикла соответствия:

find / -type d -iregex '.*/wp-content/plugins' -print0 | while read -r -d $'\0' D; do
    find "$D" -maxdepth 2 -mindepth 1
done

Причина, по которой вы можете сделать это таким образом, заключается в том, что вы можете посчитать, что это может привести к путанице / громоздкости с несколькими -execсимволами, в то же время избегая проблем с особыми именами файлов, содержащими \ n '' и т. Д. -Print0 будет использовать нулевые разделители между результатами.

Эд Невилл
источник
0

Основываясь на том, что вы начали:

find -type d -path *wp-content/plugins/* | egrep -o "wp-content/plugins/[^/]+($|/)" | sed -r "s~wp-content/plugins/([^/]+)($|/)~\1~" | uniq  

egrep захватывает * wp-content / plugins / * и любые символы до следующей косой черты / или конца строки $ - включая нужную часть.

sed с разделителем ~ выбирает нужную часть, используя первый набор скобок (), и использует \ 1 (то, что вы хотите) в качестве замены всего

Uniq отфильтровывает повторяющиеся результаты

MikeD
источник