Je veux savoir combien de fichiers normaux ont l'extension .c
dans une grande structure de répertoires complexes, et aussi combien de répertoires ces fichiers sont répartis sur. La sortie que je veux est juste ces deux nombres.
J'ai vu cette question sur la façon d'obtenir le nombre de fichiers, mais je dois également connaître le nombre de répertoires dans lesquels se trouvent les fichiers.
- Mes noms de fichiers (y compris les répertoires) peuvent avoir n'importe quel caractère; ils peuvent commencer par
.
ou-
et avoir des espaces ou des nouvelles lignes. - Je pourrais avoir des liens symboliques dont les noms se terminent par
.c
, et des liens symboliques vers des répertoires. Je ne veux pas que les liens symboliques soient suivis ou comptés, ou je veux au moins savoir si et quand ils sont comptés. - La structure du répertoire a plusieurs niveaux et le répertoire de niveau supérieur (le répertoire de travail) contient au moins un
.c
fichier.
J'ai écrit à la hâte quelques commandes dans le shell (Bash) pour les compter moi-même, mais je ne pense pas que le résultat soit exact ...
shopt -s dotglob
shopt -s globstar
mkdir out
for d in **/; do
find "$d" -maxdepth 1 -type f -name "*.c" >> out/$(basename "$d")
done
ls -1Aq out | wc -l
cat out/* | wc -l
Cela génère des plaintes concernant les redirections ambiguës, des fichiers manquants dans le répertoire en cours et déclenche des caractères spéciaux (par exemple, la sortie redirigée find
affiche les nouvelles lignes dans les noms de fichiers ) et écrit tout un tas de fichiers vides (oups).
Comment puis-je énumérer de manière fiable mes .c
fichiers et leurs répertoires contenant?
Au cas où cela aiderait, voici quelques commandes pour créer une structure de test avec de mauvais noms et des liens symboliques:
mkdir -p cfiles/{1..3}/{a..b} && cd cfiles
mkdir space\ d
touch -- i.c -.c bad\ .c 'terrible
.c' not-c .hidden.c
for d in space\ d 1 2 2/{a..b} 3/b; do cp -t "$d" -- *.c; done
ln -s 2 dirlink
ln -s 3/b/i.c filelink.c
Dans la structure résultante, 7 répertoires contiennent des .c
fichiers et 29 fichiers normaux se terminent par .c
(si dotglob
est désactivé lorsque les commandes sont exécutées) (si j'ai mal compté, faites-le moi savoir). Ce sont les chiffres que je veux.
N'hésitez pas à ne pas utiliser ce test en particulier.
NB: Les réponses dans n'importe quel shell ou autre langue seront testées et appréciées par moi. Si je dois installer de nouveaux packages, pas de problème. Si vous connaissez une solution GUI, je vous encourage à la partager (mais je n'irai peut-être pas jusqu'à installer un DE complet pour la tester) :) J'utilise Ubuntu MATE 17.10.
Réponses:
Je n'ai pas examiné la sortie avec des liens symboliques mais:
find
commande imprime le nom du répertoire de chaque.c
fichier trouvé.sort | uniq -c
will nous indique le nombre de fichiers dans chaque répertoire (lesort
peut être inutile ici, ne sais pas)sed
, je remplace le nom du répertoire par1
, éliminant ainsi tous les caractères étranges possibles, avec juste le nombre et1
restetr
d
qu'ici est essentiellement le même queNR
. J'aurais pu omettre l'insertion1
dans lased
commande, et juste imprimerNR
ici, mais je pense que c'est un peu plus clair.Jusqu'au
tr
, les données sont délimitées par NUL, à l'abri de tous les noms de fichiers valides.Avec zsh et bash, vous pouvez utiliser
printf %q
pour obtenir une chaîne entre guillemets, qui ne contiendrait pas de retour à la ligne. Donc, vous pourriez peut-être faire quelque chose comme:Cependant, même s'il
**
n'est pas censé se développer pour les liens symboliques vers les répertoires , je n'ai pas pu obtenir la sortie souhaitée sur bash 4.4.18 (1) (Ubuntu 16.04).Mais zsh a bien fonctionné, et la commande peut être simplifiée:
D
permet à ce glob de sélectionner des fichiers de points,.
sélectionne des fichiers normaux (donc pas des liens symboliques), et:h
imprime uniquement le chemin du répertoire et non le nom du fichier (commefind
le sien%h
) (voir les sections sur la génération de nom de fichier et les modificateurs ). Donc, avec la commande awk, nous avons juste besoin de compter le nombre de répertoires uniques qui apparaissent, et le nombre de lignes est le nombre de fichiers.la source
29 7
. Si j'ajoute-L
àfind
, cela remonte à41 10
. De quelle sortie avez-vous besoin?Python a
os.walk
, ce qui rend des tâches comme celle-ci faciles, intuitives et automatiquement robustes même face à des noms de fichiers étranges tels que ceux qui contiennent des caractères de nouvelle ligne. Ce script Python 3, que j'avais initialement publié dans le chat , est destiné à être exécuté dans le répertoire actuel (mais il n'a pas besoin d'être situé dans le répertoire actuel, et vous pouvez changer le chemin vers lequel il passeos.walk
):Cela affiche le nombre de répertoires contenant directement au moins un fichier dont le nom se termine
.c
, suivi d'un espace, suivi du nombre de fichiers dont le nom se termine.c
. Les fichiers "masqués" - c'est-à-dire les fichiers dont le nom commence par -.
sont inclus et les répertoires masqués sont parcourus de la même manière.os.walk
traverse récursivement une hiérarchie de répertoires. Il énumère tous les répertoires qui sont accessibles de manière récursive à partir du point de départ que vous lui donnez, fournissant des informations sur chacun d'eux sous la forme d'un tuple de trois valeurs,root, dirs, files
. Pour chaque répertoire vers lequel il passe (y compris le premier dont vous lui donnez le nom):root
contient le chemin d'accès de ce répertoire. Notez que ceci est totalement sans rapport avec « répertoire racine » du système/
(et sans rapport/root
) mais il serait aller à ceux si vous commencez là. Dans ce cas,root
commence au chemin.
--ie, le répertoire courant - et va partout en dessous.dirs
contient une liste des chemins d'accès de tous les sous - répertoires du répertoire dont le nom est actuellement conservéroot
.files
contient une liste des chemins d'accès de tous les fichiers qui résident dans le répertoire dont le nom est actuellement conservéroot
mais qui ne sont pas eux-mêmes des répertoires. Notez que cela inclut d'autres types de fichiers que les fichiers normaux, y compris les liens symboliques, mais il semble que vous ne vous attendiez pas à ce que de telles entrées se terminent par.c
et que vous souhaitiez en voir d'autres.Dans ce cas, je n'ai qu'à examiner le troisième élément du tuple
files
(que j'appellefs
dans le script). Comme lafind
commande, Pythonos.walk
traverse pour moi des sous-répertoires; la seule chose que je dois inspecter moi-même est le nom des fichiers que chacun d'eux contient. Contrairement à lafind
commande, cependant,os.walk
me fournit automatiquement une liste de ces noms de fichiers.Ce script ne suit pas de liens symboliques. Vous ne voulez probablement pas que les liens symboliques soient suivis pour une telle opération, car ils pourraient former des cycles, et parce que même s'il n'y a pas de cycles, les mêmes fichiers et répertoires peuvent être parcourus et comptés plusieurs fois s'ils sont accessibles via différents liens symboliques.
Si vous avez déjà voulu
os.walk
suivre des liens symboliques - ce que vous ne feriez pas habituellement - alors vous pouvez y passerfollowlinks=true
. Autrement dit, au lieu d'écrire,os.walk('.')
vous pourriez écrireos.walk('.', followlinks=true)
. Je réitère que vous voudriez rarement cela, en particulier pour une tâche comme celle-ci où vous énumérez récursivement une structure de répertoires entière, quelle que soit sa taille, et en comptant tous les fichiers qui répondent à certaines exigences.la source
Rechercher + Perl:
Explication
La
find
commande trouvera tous les fichiers normaux (donc pas de liens symboliques ou de répertoires), puis affichera le nom du répertoire dans lequel ils se trouvent (%h
) suivi de\0
.perl -0 -ne
: lire l'entrée ligne par ligne (-n
) et appliquer le script donné par-e
à chaque ligne. Le-0
définit le séparateur de ligne d'entrée sur\0
afin que nous puissions lire une entrée délimitée par des valeurs nulles.$k{$_}++
:$_
est une variable spéciale qui prend la valeur de la ligne courante. Ceci est utilisé comme clé du hachage%k
, dont les valeurs sont le nombre de fois où chaque ligne d'entrée (nom du répertoire) a été vue.}{
: il s'agit d'un raccourci pour écrireEND{}
. Toutes les commandes après le}{
seront exécutées une fois, une fois toutes les entrées traitées.print scalar keys %k, " $.\n"
:keys %k
retourne un tableau des clés dans le hachage%k
.scalar keys %k
donne le nombre d'éléments dans ce tableau, le nombre de répertoires vus. Ceci est imprimé avec la valeur actuelle de$.
, une variable spéciale qui contient le numéro de ligne d'entrée actuel. Comme cela est exécuté à la fin, le numéro de ligne d'entrée actuel sera le numéro de la dernière ligne, donc le nombre de lignes vues jusqu'à présent.Vous pouvez étendre la commande perl à ceci, pour plus de clarté:
la source
Voici ma suggestion:
Ce script court crée un fichier temporaire, trouve tous les fichiers dans et sous le répertoire courant se terminant par
.c
et écrit la liste dans le fichier temporaire.grep
est ensuite utilisé pour compter les fichiers (après Comment puis-je obtenir un nombre de fichiers dans un répertoire à l'aide de la ligne de commande? ) deux fois: La deuxième fois, les répertoires répertoriés plusieurs fois sont supprimés à l'aide de la suppression dessort -u
noms de fichier de chaque ligne à l'aide desed
.Cela fonctionne également correctement avec les nouvelles lignes dans les noms de fichiers:
grep -c /
ne compte que les lignes avec une barre oblique et ne considère donc que la première ligne d'un nom de fichier multiligne dans la liste.Production
la source
Petit shellscript
Je suggère un petit shellscript bash avec deux lignes de commande principales (et une variable
filetype
pour faciliter le changement afin de rechercher d'autres types de fichiers).Il ne recherche pas ou dans les liens symboliques, seulement les fichiers normaux.
Shellscript verbeux
Ceci est une version plus verbeuse qui prend également en compte les liens symboliques,
Sortie test
À partir d'un court shellscript:
Du shellscript verbeux:
la source
Doublure simple Perl one:
Ou plus simple avec la
find
commande:Si vous aimez le golf et que vous avez un Perl récent (comme il y a moins de dix ans):
la source
Pensez à utiliser la
locate
commande qui est beaucoup plus rapide que lafind
commande.Exécution sur des données de test
Merci à Muru pour sa réponse pour m'aider à supprimer les liens symboliques du nombre de fichiers dans la réponse Unix et Linux .
Merci à Terdon pour sa réponse
$PWD
(non dirigée contre moi) dans la réponse Unix et Linux .Réponse originale ci-dessous référencée par des commentaires
Forme courte:
sudo updatedb
Mettre à jour la base de données utilisée par lalocate
commande si les.c
fichiers ont été créés aujourd'hui ou si vous les avez supprimés.c
aujourd'hui.locate -cr "$PWD.*\.c$"
localisez tous les.c
fichiers dans le répertoire courant et ses enfants ($PWD
). Au lieu d'imprimer les noms de fichiers et d'imprimer le nombre avec un-c
argument. Ler
spécifie l'expression régulière au lieu de la*pattern*
correspondance par défaut, ce qui peut donner trop de résultats.locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l
. Recherchez tous les*.c
fichiers dans le répertoire actuel et ci-dessous. Supprimez le nom du fichiersed
en ne laissant que le nom du répertoire. Comptez le nombre de fichiers dans chaque répertoire en utilisantuniq -c
. Comptez le nombre de répertoires avecwc -l
.Commencez au répertoire actuel avec une ligne
Notez comment le nombre de fichiers et le nombre de répertoires ont changé. Je crois que tous les utilisateurs ont le
/usr/src
répertoire et peuvent exécuter les commandes ci-dessus avec différents nombres en fonction du nombre de noyaux installés.Forme longue:
Le formulaire long comprend le temps afin que vous puissiez voir à quel point la vitesse
locate
est terminéefind
. Même si vous devez l'exécuter,sudo updatedb
il est beaucoup plus rapide qu'un simplefind /
.Remarque: il s'agit de tous les fichiers sur TOUS les lecteurs et partitions. c'est-à-dire que nous pouvons également rechercher des commandes Windows:
J'ai trois partitions Windows 10 NTFS montées automatiquement dans
/etc/fstab
. Soyez conscient de localiser sait tout!Compte intéressant:
Il faut 15 secondes pour compter 1 637 135 fichiers dans 286 705 répertoires. YMMV.
Pour une ventilation détaillée de
locate
la gestion des regex de la commande (il semble que ce ne soit pas nécessaire dans ce Q&R mais utilisé juste au cas où), veuillez lire ceci: Utilisez "Locate" sous un répertoire spécifique?Lecture supplémentaire d'articles récents:
la source
.c
(notez qu'il se cassera s'il y a un fichier nommé-.c
dans le répertoire en cours puisque vous ne citez pas*.c
) puis il imprimera tous les répertoires dans le système, qu'ils contiennent ou non des fichiers .c.~/my_c_progs/*.c
. Il compte 638 répertoires avec des.c
programmes, le total des répertoires est affiché plus tard sous la forme286,705
. Je vais réviser la réponse en double guillemet "" * .c ". Merci pour le conseil.locate -r "/path/to/dir/.*\.c$"
, mais cela n'est mentionné nulle part dans votre réponse. Vous donnez uniquement un lien vers une autre réponse qui le mentionne, mais sans explication sur la façon de l'adapter pour répondre à la question posée ici. Toute votre réponse se concentre sur la façon de compter le nombre total de fichiers et de répertoires sur le système, ce qui n'est pas pertinent pour la question posée qui était "comment puis-je compter le nombre de fichiers .c et le nombre de répertoires contenant. fichiers c dans un répertoire spécifique ". De plus, vos numéros sont faux, essayez-le sur l'exemple du PO.$PWD
variable: unix.stackexchange.com/a/188191/200094$PWD
ne contient pas de caractères qui peuvent être spéciaux dans une expression régulière