web-dev-qa-db-fra.com

Comment répertorier récursivement tous les répertoires d'un emplacement, en premier?

La liste étendue est ici importante. Aussi, limiter la profondeur recherchée serait sympa.

$ 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

Je voudrais le faire en utilisant une doublure bash, si possible. S'il y avait un javascript-Shell, j'imagine quelque chose comme

bash("find . -type d").sort( function (x) x.findall(/\//g).length; )
28
Andrey Fedorov

La commande find prend en charge -printf option qui reconnaît de nombreux espaces réservés.

Un de ces espaces réservés est %d qui rend la profondeur du chemin donné, par rapport à l'endroit où find a commencé.

Par conséquent, vous pouvez utiliser un simple revêtement simple:

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

C'est assez simple et ne dépend pas d'outils lourds comme Perl.

Comment ça fonctionne:

  • il génère en interne une liste de fichiers, chacun rendu sous forme de ligne à deux champs
  • le premier champ contient la profondeur, qui est utilisée pour le tri numérique (inverse), puis coupé
  • le résultat est une simple liste de fichiers, un fichier par ligne, dans l'ordre le plus profond
28
Petr Kozelka

Si vous souhaitez le faire à l'aide d'outils standard, le pipeline suivant devrait fonctionner:

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

C'est,

  1. trouver et imprimer tous les répertoires ici en profondeur de premier ordre
  2. compter le nombre de barres obliques dans chaque répertoire et l'ajouter au chemin
  3. trier par profondeur (c.-à-d., nombre de barres obliques)
  4. extraire juste le chemin.

Pour limiter la profondeur trouvée, ajoutez l'argument -maxdepth à la commande find.

Si vous voulez que les répertoires soient listés dans le même ordre que ceux qui les trouvent, utilisez "sort -n -s" au lieu de "sort -n"; l'indicateur "-s" stabilise le tri (c'est-à-dire qu'il préserve l'ordre d'entrée parmi les éléments qui se comparent également).

21
Emil Sit

Mon sentiment est que c'est une meilleure solution que celles mentionnées précédemment. Cela implique grep et autres et une boucle, mais je trouve que cela fonctionne très bien, en particulier pour les cas où vous voulez que les choses soient mises en mémoire tampon et non pas la recherche complète en mémoire tampon.

Il consomme plus de ressources en raison de:

  • Beaucoup de fourches
  • Beaucoup de trouvailles
  • Chaque répertoire avant la profondeur actuelle est atteint par find autant de fois qu'il y a de profondeur totale dans la structure du fichier (cela ne devrait pas être un problème si vous avez pratiquement n'importe quelle quantité de RAM ...)

C'est bien parce que:

  • Il utilise des outils bash et gnu de base
  • Il peut être cassé quand vous le souhaitez (comme si vous voyez ce que vous cherchiez à voler)
  • Cela fonctionne par ligne et non par recherche, donc les commandes suivantes n'ont pas à attendre une recherche et un tri
  • Il fonctionne en fonction de la séparation réelle du système de fichiers, donc si vous avez un répertoire avec une barre oblique, il ne sera pas répertorié plus profondément qu'il ne l'est; si vous avez configuré un séparateur de chemin différent, tout va bien.
#!/bin/bash 
 depth = 0 
 
 tandis que find -mindepth $ depth -maxdepth $ depth | grep '.' 
 do 
 depth = $ ((depth + 1)) 
 done

Vous pouvez également l'ajuster sur une seule ligne assez (?) Facilement:

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

Mais je préfère les petits scripts à la frappe ...

5
ClashTheBunny

Vous pouvez utiliser la commande find, find/path/to/dir -type d Voici donc un exemple de liste de répertoires dans le répertoire courant:

find . -type d
5
dayanand

Je ne pense pas que vous puissiez le faire en utilisant des utilitaires intégrés, car lorsque vous parcourez une hiérarchie de répertoires, vous voulez presque toujours une recherche en profondeur d'abord, de haut en bas ou de bas en haut. Voici un script Python qui vous donnera une recherche en premier lieu:

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

Modifier:

  1. En utilisant os.path- module au lieu de os.stat- fonction et stat- module.
  2. En utilisant list.pop et list.extend au lieu de del et += les opérateurs.
5
Adam Rosenfield

J'ai essayé de trouver un moyen de le faire avec find mais il ne semble pas avoir quelque chose comme un -breadth option. À moins d'écrire un correctif pour celui-ci, essayez l'incantation Shell suivante (pour 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

J'ai en quelque sorte trébuché dessus accidentellement, donc je ne sais pas si cela fonctionne en général (je le testais uniquement sur la structure de répertoires spécifique dont vous parliez)

Si vous voulez limiter la profondeur, mettez une variable de compteur dans la boucle externe, comme ça (j'ajoute également des commentaires à celle-ci):

# 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

(remplacez le 2 dans -lt 2 avec la profondeur)

Fondamentalement, cela implémente l'algorithme de recherche standard en largeur en utilisant $LIST et $NLIST comme une file d'attente de noms de répertoires. Voici cette dernière approche en une ligne pour un copier-coller facile:

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
3
David Z

Sans l'ordre mérité: trouver -maxdepth -type d

Pour obtenir la commande méritée, vous devez faire la récursivité vous-même, avec ce petit 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"

Ensuite, vous pouvez appeler ce script avec le répertoire de base des paramètres et la profondeur.

2
ypnos

Voici une façon possible d'utiliser la recherche. Je ne l'ai pas testé à fond, alors méfiez-vous des utilisateurs ...

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
1
Rog

Quelque chose comme ça:

find . -type d | 
  Perl -lne'Push @_, $_;
    print join $/,
      sort { 
        length $a <=> length $b || 
          $a cmp $b 
        } @_ if eof'
0
Dimitre Radoulov