2009-02-12 4 views
21

Важнейшим здесь является список с шестнадцатеричным списком. Кроме того, было бы неплохо ограничить поиск глубины.Как я рекурсивно перечислить все каталоги в месте, в ширину?

$ 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 

Я бы хотел сделать это, используя по возможности одноразовый баш. Если бы не была Javascript скорлупы, я бы себе представить, что-то вроде

bash("find . -type d").sort(function (x) x.findall(/\//g).length;) 
+0

Не могли бы вы остановиться на этом, чтобы включить ваш язык по выбору, а также ОС –

+0

Arg (Linux?)! Это вопрос сообщества wiki. Раздражает. –

+0

Что делает этот вопрос вики сообщества? –

ответ

15

Команда find поддерживает -printf вариант, который распознает много заполнителей.

Одним из таких заполнителей является %d, который дает глубину данного пути относительно того, где начинается find.

Таким образом, вы можете использовать следующий простой Однострочник:

find -type d -printf '%d\t%P\n' | sort -r -nk1 | cut -f2- 

Это довольно просто, и не зависит от тяжелых инструментов, как perl.

Как это работает:

  • он внутренне генерирует список файлов, каждый из которых отображается как две линии поля в
  • первое поле содержит глубину, которая используется для (обратного) числовой сортировки, и затем срежьте
  • в результате чего простой список файлов, один файл в каждой строке, в глубочайшем-первого порядка
+0

Простой и элегантный для однострочного. Благодаря! – gnzg

2

Без заслуженного заказа: найти -maxdepth типа 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" 

Затем вы можете вызвать этот скрипт с базой данных параметров и глубиной.

+0

Это именно то, что я хочу, но с неправильным заказом. Я изменил вопрос, чтобы уточнить, спасибо! –

+0

см. Мое дополнение! Я не закончил :) – ypnos

4

Я не думаю, что вы могли бы сделать это с помощью встроенных утилит, так как при переходе по иерархии каталогов почти всегда требуется поиск по глубине, сверху вниз или снизу вверх. Вот скрипт Python, который даст вам поиск в ширину:

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:

  1. Использование os.path -модуля вместо os.stat -функции и stat -модуля.
  2. Использование list.pop и list.extend вместо del и += операторов.
3

Я пытался найти способ сделать это с find, но это не кажется, есть что-то вроде -breadth варианта. Короткие записи патч, попробуйте следующую оболочку колдовство (для Баш):

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's list 
    LIST=$NLIST; 
    # clear the next level's 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 
+1

Глядя на это снова, это определенно делает мой список «вещей, которые никогда не должны делаться в Bash» ;-) –

+0

Можете ли вы также отформатировать его не как однострочный, чтобы легче понять код ? (Но да, не делайте этого в bash :-) –

+0

Хорошо, теперь отформатирован и прокомментирован ;-) –

19

Если вы хотите сделать это с помощью стандартных инструментов, следующий конвейер должен работать:

find . -type d | perl -lne 'print tr:/::, " $_"' | sort -n | cut -d' ' -f2 

То есть,

  1. находкой и распечатать все каталоги здесь, в глубине первого порядка
  2. подсчитывать количество косых черт в каждом каталоге и PREPEND его пути
  3. сортировать по глубине (т. Е. Число косых черт)
  4. экстракт только путь.

Чтобы ограничить найденную глубину, добавьте аргумент -maxdepth в команду find.

Если вы хотите, чтобы каталоги, перечисленные в том же порядке, что и вывести их, используйте «sort -n -s» вместо «sort -n»; флаг «-s» стабилизирует сортировку (т. е. сохраняет порядок ввода среди элементов, которые сравниваются одинаково).

+0

Добавьте «2>/dev/null» в команду find, т. Е. find. -type d 2>/dev/null Обеспечит, чтобы ошибка поиска не испортила результаты. –

+0

Как насчет сортировки в алфавитном порядке? – mr5

+0

Не работает, если у dirnames есть пробелы. Например, если dir является «/ data/Mundial/Trinidad \ y \ Tobago /», у вас будет только «/ data/arbol/Mundial/Trinidad». – alemol

1

Вот возможный способ, используя поиск. Я тщательно не проверял, так что пользователь остерегайтесь ...

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 
0

Что-то вроде этого:

find . -type d | 
    perl -lne'push @_, $_; 
    print join $/, 
     sort { 
     length $a <=> length $b || 
      $a cmp $b 
     } @_ if eof' 
5

У меня такое ощущение, что это лучшее решение, чем ранее упомянутых единиц. Это связано с grep и т. П., Но я считаю, что он работает очень хорошо, особенно для случаев, когда вы хотите, чтобы строки были буферизированы, а не полностью заполнены буфером поиска.

Это более ресурсоемким из:

  • Много разветвление
  • Много находок
  • Каждый каталог до текущей глубины бьет по найти столько раз, сколько есть общая глубина до Файловая структура (это не должно быть проблемой, если у вас есть практически любое количество оперативной памяти ...)

Это хорошо, потому что:

  • Он использует Баш и основные Gnu инструменты
  • Это может быть разорвано всякий раз, когда вы хотите (как вы видите, что вы искали лету)
  • Он работает на каждую строку, а не за находку, поэтому последующие команды не» t должен ждать поиска и сортировки
  • Он работает на основе фактического разделения файловой системы, поэтому, если у вас есть каталог с косой чертой, он не будет отображаться глубже, чем он есть; если у вас есть другой разделитель путей, вы все равно в порядке.
#!/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 

Но я предпочитаю небольшие сценарии более типирования ...

3

Вы можете использовать найти команду, находкой/путь/к/реж -типа d Так ниже пример списка каталогов в текущем каталоге:

find . -type d