Trouver le nombre de fichiers pour chaque extension dans un répertoire

10

Je veux compter le nombre de fichiers pour chaque extension dans un répertoire ainsi que les fichiers sans extension.

J'ai essayé quelques options, mais je n'ai pas encore trouvé de solution de travail:

  • find "$folder" -type f | sed 's/.*\.//' | sort | uniq -cest une option mais ne fonctionne pas s'il n'y a pas d'extension de fichier. J'ai besoin de savoir combien de fichiers n'ont pas d'extension.

  • J'ai également essayé une boucle de recherche dans un tableau, puis additionner les résultats, mais à ce moment, ce code génère une erreur de variable non déclarée, mais uniquement en dehors de la boucle:

    declare -a arr
    arr=()
    echo ${arr[@]}
    

    Cela lève une variable non déclarée, ainsi qu'une fois la boucle de recherche terminée.

garçon de tracteur
la source

Réponses:

10
find "$path" -type f | sed -e '/.*\/[^\/]*\.[^\/]*$/!s/.*/(none)/' -e 's/.*\.//' | LC_COLLATE=C sort | uniq -c

Explication:

  • find "$path" -type f obtenir une liste récursive de tous les fichiers du "$path"dossier.
  • sed -e '/.*\/[^\/]*\.[^\/]*$/!s/.*/(none)/' -e 's/.*\.//' expressions régulières:
    • /.*\/[^\/]*\.[^\/]*$/!s/.*/(none)/ remplacer tous les fichiers sans extension par (aucun).
    • s/.*\.// obtenir l'extension des fichiers restants.
  • LC_COLLATE=C sort trier le résultat, en gardant les symboles en haut.
  • uniq -c compter le nombre d'entrées répétées.
Hélio
la source
9

Utilisation de Python:

import os
from collections import Counter
from pprint import pprint

lst = []
for file in os.listdir('./'):
        name, ext = os.path.splitext(file)
        lst.append(ext)

pprint(Counter(lst))

Le résultat:

Counter({'': 7,
         '.png': 4,
         '.mp3': 3,
         '.jpg': 3,
         '.mkv': 3,
         '.py': 1,
         '.swp': 1,
         '.sh': 1})
Ravexina
la source
Vous pouvez probablement vous en tirer avec la compréhension de la liste, comme ext = [ f.split('.')[-1] for f in os.listdir('./') ] Thatll, ce qui rendra les lignes plus courtes et peut-être plus Pythonic
Sergiy Kolodyazhnyy
Merci pour la suggestion, j'essayais juste de l'écrire aussi clairement que possible ...
Ravexina
1
La clarté est la vertu :) Surtout en ce qui concerne le code et la documentation technique.
Sergiy Kolodyazhnyy
6

Si vous avez GNU awk, vous pouvez faire quelque chose comme

printf '%s\0' * | gawk 'BEGIN{RS="\0"; FS="."; OFS="\t"} 
  {a[(NF>1 ? $NF : "(none)")]++} 
  END{for(i in a) print a[i],i}
'

c'est-à-dire construire / incrémenter un tableau associatif sur le dernier .champ séparé, ou une chaîne fixe arbitraire comme (none)s'il n'y a pas d'extension.

mawkne semble pas autoriser un séparateur d'enregistrements null-byte - vous pouvez utiliser mawkavec le séparateur de saut de ligne par défaut si vous êtes sûr de ne pas avoir à traiter les sauts de ligne dans les noms de vos fichiers:

printf '%s\n' * | mawk 'BEGIN{FS="."; OFS="\t"} {a[(NF>1 ? $NF : "(none)")]++} END{for(i in a) print a[i],i}'
tournevis
la source
5

Avec la base /bin/shou même bashla tâche peut être un peu difficile, mais comme vous pouvez le voir dans d'autres réponses, les outils qui peuvent travailler sur des données agrégées peuvent traiter une telle tâche particulièrement facile. Un tel outil serait la sqlitebase de données.

Le processus très simple pour utiliser la sqlitebase de données serait de créer un .csvfichier avec deux champs: nom de fichier et extension. Plus tard, vous sqlitepouvez utiliser une instruction d'agrégation simple COUNT()avec GROUP BY extpour effectuer le comptage des fichiers en fonction du champ d'extension

$ { printf "file,ext\n"; find -type f -exec sh -c 'f=${1##*/};printf "%s,%s\n" "${1}" "${1##*.}"' sh {} \; ; }  > files.csv
$ sqlite3 <<EOF
> .mode csv
> .import ./files.csv files_tb
> SELECT ext,COUNT(file) FROM files_tb GROUP BY ext;
> EOF
csv,1
mp3,6
txt,1
wav,27
Sergiy Kolodyazhnyy
la source
files_tbJe pense que la table est référencée mais les colonnes de la table ne sont définies nulle part où je peux voir?
WinEunuuchs2Unix
@ WinEunuuchs2Unix Ils sont définis dans le fichier csv lui-même. C'est ce que fait le premier printf. Et SQLite par défaut traitera la première ligne du fichier csv comme des noms de colonne.
Sergiy Kolodyazhnyy
1
Très impressionnant! +1
WinEunuuchs2Unix
5

Utilisation de PowerShell si c'est une option:

Get-ChildItem -File | Group-Object Extension -NoElement

ou plus court, en utilisant des alias:

ls -file | group -n Extension
Joey
la source
1
Hou la la! Grande première réponse! Je ne savais même pas que PowerShell existait pour Linux ... +1
Fabby
2
Merci. Il existe depuis longtemps un environnement multiplateforme et open source, mais il y a eu un modèle sur SO et SU où les questions pour les scripts shell sur Windows ont souvent été répondues par "Eh bien, installez cygwin et utilisez bash, alors vous pouvez faire ce qui suit ", j'ai donc hésité à faire de même pour les sites Linux SE avec des outils provenant de Windows. Mais cela a été une belle tâche qui montre assez bien les forces de PowerShell sans inviter l'ancien argument sur la verbosité.
Joey