Как я рекурсивно перечислить все каталоги в одном месте, в ширину?
Здесь очень важен список с шириной в ширину. Кроме того, было бы неплохо ограничить поиск глубины.
$ find . -type d
/foo
/foo/subfoo
/foo/subfoo/subsub
/foo/subfoo/subsub/subsubsub
/bar
/bar/subbar
$ find . -type d -depth
/foo/subfoo/subsub/subsubsub
/foo/subfoo/subsub
/foo/subfoo
/foo
/bar/subbar
/bar
$ < what goes here? >
/foo
/bar
/foo/subfoo
/bar/subbar
/foo/subfoo/subsub
/foo/subfoo/subsub/subsubsub
Я хотел бы сделать это, используя bash однострочный, если это возможно. Если бы была javascript-оболочка, я бы предположил что-то вроде
bash("find . -type d").sort( function (x) x.findall(/\//g).length; )
Ответы
Ответ 1
Команда find
поддерживает опцию -printf
, которая распознает много заполнителей.
Один такой заполнитель %d
, который отображает глубину заданного пути относительно начала find
.
Поэтому вы можете использовать следующий простой однострочный:
find -type d -printf '%d\t%P\n' | sort -r -nk1 | cut -f2-
Это довольно просто и не зависит от тяжелых инструментов, таких как perl
.
Как это работает:
- он внутренне генерирует список файлов, каждый из которых отображается как строка с двумя полями.
- первое поле содержит глубину, которая используется для (обратного) численного сортирования, а затем разрезает
- получается простой список файлов, по одному файлу на строку, в самом глубоком первом порядке
Ответ 2
Если вы хотите сделать это с помощью стандартных инструментов, следующий трубопровод должен работать:
find . -type d | perl -lne 'print tr:/::, " $_"' | sort -n | cut -d' ' -f2
То есть
- найти и распечатать все каталоги здесь в глубину первого порядка
- подсчитайте количество косых черт в каждом каталоге и добавьте его в путь
- сортировать по глубине (т.е. количество косых черт)
- извлеките только путь.
Чтобы ограничить найденную глубину, добавьте аргумент -maxdepth в команду find.
Если вам нужны каталоги, перечисленные в том же порядке, что и их вывод, используйте "sort -n -s" вместо "sort -n"; флаг "-s" стабилизирует сортировку (т.е. сохраняет порядок ввода среди элементов, которые сравниваются одинаково).
Ответ 3
Я чувствую, что это лучшее решение, чем предыдущие. Он включает в себя grep и т.п., Но я считаю, что он работает очень хорошо, особенно для случаев, когда вы хотите, чтобы строки были буферизированы, а не полностью заполнены буфером поиска.
Это более ресурсоемкий из-за:
- Множество разметки
- Много находок
- Каждый каталог до текущей глубины попадает, набирая столько раз, сколько будет общей глубины файловой структуры (это не должно быть проблемой, если у вас есть практически любое количество бара...)
Это хорошо, потому что:
- Он использует bash и основные инструменты gnu
- Его можно сломать всякий раз, когда вы хотите (например, вы видите, что искали, чтобы летать)
- Он работает на строку, а не на поиск, поэтому последующим командам не нужно ждать поиска и сортировки
- Он работает на основе фактического разделения файловой системы, поэтому, если у вас есть каталог с косой чертой, он не будет отображаться глубже, чем он есть; если у вас есть другой разделитель путей, вы все равно в порядке.
#!/bin/bash
depth=0
while find -mindepth $depth -maxdepth $depth | grep '.'
do
depth=$((depth + 1))
done
Вы также можете легко поместить его на одну строку (?):
depth=0; while find -mindepth $depth -maxdepth $depth | grep --color=never '.'; do depth=$((depth + 1)); done
Но я предпочитаю небольшие скрипты по набору текста...
Ответ 4
Я не думаю, что вы могли бы сделать это с помощью встроенных утилит, так как при переходе по иерархии каталогов почти всегда требуется поиск по глубине, как сверху вниз, так и снизу вверх. Здесь Python script, который даст вам поиск в ширину:
import os, sys
rootdir = sys.argv[1]
queue = [rootdir]
while queue:
file = queue.pop(0)
print(file)
if os.path.isdir(file):
queue.extend(os.path.join(file,x) for x in os.listdir(file))
Edit:
- Использование
os.path
-модуля вместо os.stat
-функции и stat
-модуля.
- Использование
list.pop
и list.extend
вместо del
и +=
операторов.
Ответ 5
Я попытался найти способ сделать это с помощью find
, но он, похоже, не имеет ничего похожего на параметр -breadth
. За исключением написания патча для него, попробуйте следующее заклинание оболочки (для bash):
LIST="$(find . -mindepth 1 -maxdepth 1 -type d)";
while test -n "$LIST"; do
for F in $LIST; do
echo $F;
test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)";
done;
LIST=$NLIST;
NLIST="";
done
Я как бы случайно наткнулся на это, поэтому не знаю, работает ли он вообще (я тестировал его только по конкретной структуре каталогов, о которой вы просили)
Если вы хотите ограничить глубину, поставьте переменную счетчика во внешнем цикле, например (я также добавляю комментарии к этому):
# initialize the list of subdirectories being processed
LIST="$(find . -mindepth 1 -maxdepth 1 -type d)";
# initialize the depth counter to 0
let i=0;
# as long as there are more subdirectories to process and we haven't hit the max depth
while test "$i" -lt 2 -a -n "$LIST"; do
# increment the depth counter
let i++;
# for each subdirectory in the current list
for F in $LIST; do
# print it
echo $F;
# double-check that it is indeed a directory, and if so
# append its contents to the list for the next level
test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)";
done;
# set the current list equal to the next level list
LIST=$NLIST;
# clear the next level list
NLIST="";
done
(замените 2 на -lt 2
на глубину)
В основном это реализует стандартный алгоритм поиска по ширине с использованием $LIST
и $NLIST
в качестве очереди имен каталогов. Здесь последний подходит как однострочный для простого копирования и вставки:
LIST="$(find . -mindepth 1 -maxdepth 1 -type d)"; let i=0; while test "$i" -lt 2 -a -n "$LIST"; do let i++; for F in $LIST; do echo $F; test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)"; done; LIST=$NLIST; NLIST=""; done
Ответ 6
Вы можете использовать команду find,
find/path/to/dir-type d
Итак, ниже пример списка каталогов в текущем каталоге:
find . -type d
Ответ 7
Без заслуженного заказа: find -maxdepth-type d
Чтобы получить заслуженное упорядочение, вы должны сделать рекурсию самостоятельно, с помощью этого небольшого shellscript:
#!/bin/bash
r ()
{
let level=$3+1
if [ $level -gt $4 ]; then return 0; fi
cd "$1"
for d in *; do
if [ -d "$d" ]; then
echo $2/$d
fi;
done
for d in *; do
if [ -d "$d" ]; then
(r "$d" "$2/$d" $level $4)
fi;
done
}
r "$1" "$1" 0 "$2"
Затем вы можете вызвать этот script с базовым каталогом параметров и глубиной.
Ответ 8
Вот возможный способ, используя find. Я не тестировал его полностью, поэтому пользователь должен быть осторожен...
depth=0
output=$(find . -mindepth $depth -maxdepth $depth -type d | sort);
until [[ ${#output} -eq 0 ]]; do
echo "$output"
let depth=$depth+1
output=$(find . -mindepth $depth -maxdepth $depth -type d | sort)
done
Ответ 9
Что-то вроде этого:
find . -type d |
perl -lne'push @_, $_;
print join $/,
sort {
length $a <=> length $b ||
$a cmp $b
} @_ if eof'