Como faço para listar recursivamente todos os diretórios em um local, em largura?

StackOverflow https://stackoverflow.com/questions/539583

  •  22-08-2019
  •  | 
  •  

Pergunta

Amplitude-primeira lista é importante, aqui. Além disso, a limitação da profundidade procurou seria bom.

$ 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

Eu gostaria de fazer isso usando uma festança de uma linha, se possível. Se houvesse uma javascript-shell, eu imagino algo como

bash("find . -type d").sort( function (x) x.findall(/\//g).length; )
Foi útil?

Solução

A opção find suportes comando -printf que reconhece um monte de espaços reservados.

Um tal espaço reservado é %d que torna a profundidade de determinado caminho, em relação a onde find começou.

Portanto, você pode usar o seguinte de uma linha simples:

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

É muito simples, e não depende de ferramentas pesado como perl.

Como funciona:

  • -lo lista de arquivos, cada um processado como uma linha de duas campo gera internamente
  • o primeiro campo contém a profundidade, o que é usado para (reverso) a classificação numérica, e, em seguida, cortada
  • resultante é simples listagem de arquivo, um arquivo por linha, no mais profundo-primeira ordem

Outras dicas

Se você quiser fazê-lo usando ferramentas padrão, a seguinte gasoduto deve funcionar:

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

Isto é,

  1. encontrar e imprimir todos os diretórios aqui em primeira ordem de profundidade
  2. contar o número de barras em cada diretório e preceder-lo para o caminho
  3. ordenar por profundidade (ou seja, número de barras)
  4. extrair apenas o caminho.

Para limitar a profundidade encontrada, adicione o argumento -maxdepth para o comando find.

Se você quiser que os diretórios listados na mesma ordem que a descoberta de saída-los, use "tipo -n-s" em vez de "tipo-n"; o "-s" bandeira estabiliza o tipo (ou seja, preserva a ordem de entrada entre os itens que comparar igualmente).

Eu não acho que você poderia fazê-lo usando built-in utilitários, desde quando atravessando uma hierarquia de diretórios que você quer quase sempre uma busca em profundidade, ou de cima para baixo ou de baixo para cima. Aqui está um script Python que vai lhe dar uma busca em largura:

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))

Editar:

  1. Usando os.path-módulo em vez de os.stat-função e stat-módulo.
  2. Usando list.pop e list.extend em vez de operadores del e +=.

O meu sentimento é que esta é uma solução melhor do que os mencionados anteriormente. Trata-se de grep e tal e um loop, mas eu acho que funciona muito bem, especificamente para os casos em que você quer as coisas linha tamponada e não a descoberta completa tamponada.

É mais recurso intensivo devido a:

  • Muita bifurcação
  • Muitos dos achados
  • Cada diretório antes de a profundidade atual é atingido por encontrar tantas vezes como há profundidade total para a estrutura do arquivo (isso não deve ser um problema se você tem praticamente qualquer quantidade de ram ...)

Isso é bom porque:

  • Ele usa bash e ferramentas básicas gnu
  • Ele pode ser quebrado quando quiser (como você vê o que você estava procurando fly by)
  • Ele funciona por linha e não por achado, então os comandos subseqüentes não tem que esperar por uma descoberta e uma espécie
  • Ele funciona com base na separação real do sistema de arquivos, então se você tem um diretório com uma barra nele, ele não será listado mais profundo do que é; se você tem um separador de caminho diferente configurado, você ainda são muito bem.
#!/bin/bash 
depth=0

while find -mindepth $depth -maxdepth $depth | grep '.'
do
    depth=$((depth + 1))
done

Você também pode se encaixar em uma linha bastante facilidade (?):

depth=0; while find -mindepth $depth -maxdepth $depth | grep --color=never '.'; do depth=$((depth + 1)); done

Mas eu prefiro pequenos scripts sobre digitação ...

Você pode usar o comando find, encontrar / path / to / dir do tipo d Portanto, a seguir exemplo lista de diretórios no diretório atual:

find . -type d

Eu tentei encontrar uma maneira de fazer isso com find mas não parecem ter qualquer coisa como uma opção -breadth. Curto de escrever um patch para ele, tente o seguinte encantamento shell (para 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

Eu meio que tropeçou em este acidentalmente, então eu não sei se ele funciona em geral (eu estava testando-o apenas na estrutura de diretório específico que você estava perguntando sobre)

Se você quiser limitar a profundidade, coloque uma variável de contador no loop externo, como assim (eu também estou adicionando comentários a este):

# 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

(substitua a 2 em -lt 2 com a profundidade)

Basicamente, este implementa o algoritmo de busca em largura padrão usando $LIST e $NLIST como uma fila de nomes de diretório. Aqui é a última abordagem como um one-liner para fácil de copiar e colar:

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

Sem a ordenação merecido: encontrar -maxdepth do tipo d

Para obter a ordenação merecido, você tem que fazer a recursão-se, com este pequeno 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"

Em seguida, você pode chamar esse script com pasta de base parâmetros e profundidade.

Aqui está uma maneira possível, usando find. Eu não tenho exaustivamente testado-lo, assim que o usuário tenha cuidado ...

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

Algo parecido com isto:

find . -type d | 
  perl -lne'push @_, $_;
    print join $/,
      sort { 
        length $a <=> length $b || 
          $a cmp $b 
        } @_ if eof'
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top