Répertoires contenant deux fichiers ou plus

11

Je veux trouver un sous-répertoire du répertoire courant, qui (c'est-à-dire le sous-répertoire) contient 2 fichiers réguliers ou plus.

Je ne suis pas intéressé par les répertoires contenant moins de 2 fichiers, ni par les répertoires qui ne contiennent que des sous-répertoires.

porton
la source

Réponses:

12

Voici une approche complètement différente basée sur GNU findet uniq. C'est beaucoup plus rapide et beaucoup plus convivial que les réponses basées sur l'exécution d'une commande shell qui compte les fichiers pour chaque répertoire trouvé.

find . -type f -printf '%h\n' | sort | uniq -d

La findcommande imprime le répertoire de tous les fichiers de la hiérarchie et uniqn'affiche que les répertoires qui apparaissent au moins deux fois.

xhienne
la source
2
Vous ne devez pas analyser la sortie de find. Dans ce cas, parce que GNU findva modifier les noms des répertoires qui ont des caractères qui ne sont pas imprimables dans les paramètres régionaux actuels (comme "ä" dans les paramètres régionaux C). Voir aussi unix.stackexchange.com/questions/321697/…
Kusalananda
4
@Kusalananda, pas quand la sortie ne va pas à un tty. Ici, le seul problème est avec les caractères de nouvelle ligne, que vous pouvez résoudre en utilisant-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Stéphane Chazelas
6
find . -type d \
    -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' \
    -print

Cela trouvera tous les noms dans ou sous le répertoire courant, puis filtrera tous les noms qui ne sont pas des noms de répertoires.

Les noms de répertoire restants seront donnés à ce court script:

c=0
for n in "$1"/*; do
    [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 ))
done

[ "$c" -ge 2 ]

Ce script comptera le nombre de fichiers normaux (en sautant les liens symboliques) dans le répertoire donné comme premier argument de ligne de commande (à partir de find). La dernière commande du script est un test pour voir si le nombre était de 2 ou plus. Le résultat de ce test est la valeur de retour (état de sortie) du script.

Si le test a réussi, -printentraînera l' findimpression du chemin d'accès au répertoire.

Pour également considérer les fichiers cachés (fichiers dont les noms commencent par un point), changez le sh -cscript de dire

for n in "$1"/*; do

à

for n in "$1"/* "$1"/.*; do

Essai:

$ tree
.
`-- test
    |-- a
    |-- dir1
    |   |-- a
    |   |-- b
    |   `-- c
    `-- dir2
        |-- dira
        |-- dirb
        |   |-- file-1
        |   `-- file-2
        `-- dirc

6 directories, 6 files

$ find . -type d -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' -print
./test/dir1
./test/dir2/dirb
Kusalananda
la source
Votre solution ne compte pas les fichiers dont le nom commence par un point. Vous devez également initialiser c = 0 afin d'éviter les messages d'erreur avec des répertoires qui ne contiennent aucun fichier.
xhienne
@xhienne J'ai considéré les fichiers cachés et j'ajouterai une note à ce sujet. Il n'y a pas d'erreur s'il n'y a pas de fichiers normaux dans un répertoire car [ "" -ge 2 ]c'est un test valide.
Kusalananda
Je ne sais pas comment vous définissez "valide". POSIX requiert que arg1 soit une valeur entière. dash, bash --posixet testtous affichent un message d'erreur et quittent avec 2 (c.-à-d. "Une erreur s'est produite")
xhienne
@xhienne Ah, je testais un système kshfonctionnant sous le nom de mas sh. Se modifiera immédiatement. Merci de m'avoir piqué! :-)
Kusalananda
De plus, il [ -f ... ]déréférence les liens symboliques. Vous devez ajouter un test pour les éliminer car la question spécifie que seuls les fichiers normaux doivent être comptés.
xhienne
6

Avec l'aide de la réponse de Gilles sur SU et son revers et quelques modifications, voici ce dont vous avez besoin.

find . -type d -exec sh -c 'set -- "$1"/*;X=0; 
    for args; do [ -f "$args" ] && X=$((X+1)) ;done; [ "$X" -gt 1 ] ' _ {} \; -print

Arborescence de répertoires.

.
├── test
│   ├── dir1
│   │   ├── a
│   │   ├── b
│   │   └── c
│   ├── dir2
│   │   ├── dira
│   │   │   └── a file\012with\012multiple\012line
│   │   ├── dirb
│   │   │   ├── file-1
│   │   │   └── file-2
│   │   └── dirc
│   ├── diraa
│   ├── dirbb
│   ├── dircc
│   └── x
│   └── x1
│   └── x2
└── test2
    ├── dir3
    └── dir4

Résultat:

./test
./test/dir1
./test/dir2/dirb
αғsнιη
la source
J'ai eu cela au début aussi, mais vous aurez un problème avec les répertoires contenant plusieurs sous - répertoires et fichiers. Il n'élimine pas non plus les répertoires contenant uniquement des sous-répertoires.
Kusalananda
Cela ne le résout pas vraiment. Il trouve à la fois le testet les dir2répertoires dans ma configuration de test (voir ma réponse).
Kusalananda
Fonctionne pour votre exemple, mais ajoutez test/x1et en test/x2tant que fichiers également ... $1et $2seront des répertoires pour test, et le répertoire sera manqué.
Kusalananda
@Kusalananda Aucun moyen que j'ai trouvé, sauf ce que vous avez répondu, j'ai essayé de changer une partie de ma commande pour ne pas être la copie exacte de la vôtre (je n'ai pas exclu les fichiers cachés comme vous l'avez fait), mes excuses.
αғsнιη
1
Aucun souci :-)
Kusalananda
3

Une autre approche find+ wc:

find path/currdir -maxdepth 1 -type d ! -empty ! -path "path/currdir" \
-exec sh -c 'count=$(find "$1" -maxdepth 1 -type f | wc -l); [ $count -ge 2 ]' _ {} \; -print

  • path/currdir - chemin vers votre répertoire actuel

  • -maxdepth 1- ne considérer que les sous-dossiers enfants directs

  • ! -empty - ignorer les sous-dossiers vides

  • ! -path "path/currdir" - ignorer le chemin du répertoire courant

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countest attribué avec le nombre de fichiers pour chaque sous-dossier trouvé

  • [ $count -ge 2 ] ... -print - imprimer le nom / chemin du sous-dossier contenant 2 fichiers réguliers ou plus

RomanPerekhrest
la source