Compter les lignes de code?

24

si je veux compter les lignes de code, la chose triviale est

cat *.c *.h | wc -l

Mais que faire si j'ai plusieurs sous-répertoires?

Niklas
la source
3
Hors sujet: Pourquoi l'inutile cat? wc -l *.c *.hfait la même chose.
Thomas Padron-McCarthy
5
@ ThomasPadron-McCarthy Non, ce n'est pas le cas. Vous auriez besoin wc -l *.c *.h | tail -n 1d'obtenir une sortie similaire.
Gilles 'SO- arrête d'être méchant'
2
Notez que certains (peut-être même la plupart) des shells modernes (Bash v4, Zsh, probablement plus) fournissent un mécanisme de globalisation récursive en utilisant **, donc vous auriez pu utiliser wc -l **/*.{h,c}ou quelque chose de similaire. Notez que dans Bash, au moins, cette option (appelée globstar) est désactivée par défaut. Mais notez également que dans ce cas particulier, clocou SLOCCountest une bien meilleure option. (En outre, il ackpeut être préférable de findtrouver / répertorier facilement les fichiers source.)
Kyle Strand
5
wc -l compte les lignes, pas les lignes de code. 7000 lignes vides s'afficheront toujours dans wc -l mais ne compteront pas dans une métrique de code. (les commentaires ne comptent généralement pas)
coteyr

Réponses:

49

La façon la plus simple est d'utiliser l'outil appelé cloc. Utilisez-le de cette façon:

cloc .

C'est ça. :-)

Ho1
la source
1
-1 parce que ce programme n'a aucun moyen de reconnaître des lignes de code dans des langages en dehors de son petit cerveau ennuyeux. Il connaît Ada et Pascal et C et C ++ et Java et JavaScript et les langages de type "entreprise", mais il refuse de compter le SLOC par simple extension de fichier, et est donc complètement inutile pour les DSL, ou même les langages qu'il se trouve juste ne pas connaître à propos.
cat
21
@cat Rien n'est parfait et rien ne peut répondre à toutes vos demandes passées et futures.
Ho1
2
Eh bien, le langage de programmation que CLOC refuse de reconnaître remplit bien toutes mes demandes passées et futures :)
cat
6
@cat selon la documentation CLOC qu'il peut lire dans un fichier de définition de langue, il existe donc un moyen de lui faire reconnaître le code dans les langues qu'il n'a pas définies. De plus, il est open source, vous pouvez donc toujours l'étendre pour l'améliorer!
Centimane
39

Vous devriez probablement utiliser SLOCCount ou cloc pour cela, ils sont spécialement conçus pour compter les lignes de code source dans un projet, quelle que soit la structure du répertoire, etc. non plus

sloccount .

ou

cloc .

produira un rapport sur tout le code source à partir du répertoire courant.

Si vous souhaitez utiliser findet wc, GNU wca une belle --files0-fromoption:

find . -name '*.[ch]' -print0 | wc --files0-from=-

(Merci à SnakeDoc pour la suggestion de cloc !)

Stephen Kitt
la source
+1 pour sloccount. Fait intéressant, l'exécution sloccount /tmp/stackexchange(créée à nouveau le 17 mai après mon dernier redémarrage) indique que le coût estimé pour développer les fichiers sh, perl, awk, etc. qu'elle a trouvé est de 11 029 $. et cela n'inclut pas les one-liners qui ne l'ont jamais fait dans un fichier script.
cas
11
Estimer le coût en fonction des lignes de code? Qu'en est-il de toutes les personnes employées pour reconditionner les spaghettis en quelque chose de maintenable?
Arrêtez de nuire à Monica le
@OrangeDog, vous pouvez toujours essayer de tenir compte de cela dans les frais généraux; voir la documentation pour une explication du calcul (avec des données de salaire très anciennes) et les paramètres que vous pouvez modifier.
Stephen Kitt
5
clocc'est bien aussi: github.com/AlDanial/cloc
SnakeDoc
@StephenKitt> toujours, le problème principal est qu'il compte à rebours. Lors du nettoyage du code, vous vous retrouvez souvent avec moins de lignes. Bien sûr, vous pouvez essayer de passer une surcharge pour supporter le reste du code pour tenir compte de celui supprimé, mais je ne vois pas comment c'est mieux que de deviner le prix entier en premier lieu.
spectras
10

Comme la wccommande peut prendre plusieurs arguments, vous pouvez simplement passer tous les noms de fichiers à l' wcaide de l' +argument de l' -execaction de GNU find:

find . -type f -name '*.[ch]' -exec wc -l {} +

Alternativement, dans bash, en utilisant l'option shell globstarpour parcourir les répertoires de manière récursive:

shopt -s globstar
wc -l **/*.[ch]

D'autres shells traversent récursivement par défaut (par exemple zsh) ou ont des options similaires comme globstar, enfin, au moins la plupart.

heemayl
la source
1
+1 pour ne pas avoir besoin d'installer de logiciel non standard sur une machine où je n'ai pas de racine
Bamboomy
5

Vous pouvez utiliser findavec xargset wc:

find . -type f -name '*.h' -o -name '*.c' | xargs wc -l
coffeMug
la source
2
(cela suppose que les chemins de fichiers ne contiennent pas de blancs, de retours à la ligne, de guillemets simples, de guillemets doubles de caractères barre oblique inversée. Il peut également produire plusieurs totallignes si plusieurs wcs sont invoqués.)
Stéphane Chazelas
Peut-être le plusieurs wcproblèmes de commandes peut être résolu par la tuyauterie findà la while read FILENAME; do . . .donestructure. Et à l'intérieur de la boucle while wc -l. Le reste résume le total des lignes dans une variable et l'affiche.
Sergiy Kolodyazhnyy
5

Si vous êtes dans un environnement où vous n'avez pas accès à clocetc, je vous suggère

find -name '*.[ch]' -type f -exec cat '{}' + | grep -c '[^[:space:]]'

Traversée: findrecherche récursivement tous les fichiers normaux dont le nom se termine par .cou .het s'exécute catsur eux. La sortie est canalisée greppour compter toutes les lignes non vides (celles qui contiennent au moins un caractère non espacé).

Kotte
la source
4

Comme cela a été souligné dans les commentaires, cat file | wc -ln'est pas équivalent à wc -l fileparce que le premier imprime uniquement un numéro tandis que le second affiche un numéro et le nom du fichier. De même cat * | wc -l, imprimera juste un nombre, tandis wc -l *qu'imprimera une ligne d'informations pour chaque fichier.

Dans un esprit de simplicité, revenons à la question effectivement posée:

si je veux compter les lignes de code, la chose triviale est

cat *.c *.h | wc -l

Mais que faire si j'ai plusieurs sous-répertoires?

Tout d'abord, vous pouvez simplifier même votre commande triviale pour:

cat *.[ch] | wc -l

Et enfin, l'équivalent de plusieurs sous-répertoires est:

find . -name '*.[ch]' -exec cat {} + | wc -l

Cela pourrait peut-être être amélioré de plusieurs façons, par exemple en restreignant les fichiers correspondants aux fichiers normaux uniquement (pas aux répertoires) en ajoutant -type f—mais la findcommande donnée est l' équivalent récursif exact de cat *.[ch].

Caractère générique
la source
3

Échantillon en utilisant awk:

find . -name '*.[ch]' -exec wc -l {} \; |
  awk '{SUM+=$1}; END { print "Total number of lines: " SUM }'
Lambert
la source
Utiliser +à la place de \;.
Jonathan Leffler
@JonathanLeffler Pourquoi?
Hastur
1
@Hastur: Il fonctionne wc -lpour des groupes de fichiers, un peu comme le xargsfait le cas, mais il gère les caractères impairs (comme les espaces) dans les noms de fichiers sans avoir besoin ni des options xargs(non standard) -print0ni des -0options de findet xargsrespectivement. C'est une optimisation mineure. L'inconvénient serait que chaque invocation de wcproduirait un nombre total de lignes à la fin lorsque plusieurs fichiers awkseraient fournis - le script aurait géré cela. Ce n'est donc pas un slam-dunk, mais très souvent, utiliser +à la place de \;avec findest une bonne idée.
Jonathan Leffler
@JonathanLeffler Merci. Je suis d'accord. Cependant, mes préoccupations concernaient la longueur de la chaîne de paramètres transmise à wc. Si le nombre de fichiers qui seront trouvés a priori est inconnu , y a-t-il un risque de dépasser cette limite ou est-il géré par find?
Hastur
2
@Hastur: findregroupe les fichiers en groupes de taille pratique, qui ne dépasseront pas la limite de longueur pour la liste d'arguments sur la plate-forme, permettant l'environnement (qui sort de la longueur de la liste d'arguments - donc la longueur de la liste d'arguments plus la la longueur de l'environnement doit être inférieure à une valeur maximale). IOW, findfait le bon travail, comme le xargsfait bien le travail.
Jonathan Leffler
1

commande facile:

find . -name '*.[ch]' | xargs wc -l
malyy
la source
(cela suppose que les chemins de fichiers ne contiennent pas de blancs, de retours à la ligne, de guillemets simples, de guillemets doubles de caractères barre oblique inversée. Il peut également produire plusieurs totallignes si plusieurs wcs sont invoqués.)
Stéphane Chazelas
0

Si vous êtes sous Linux, je recommande mon propre outil, polyglotte . C'est beaucoup plus rapide que clocet plus fonctionnel que sloccount.

Vous devriez également pouvoir construire sur BSD, bien qu'il n'y ait pas de binaires fournis.

Vous pouvez l'invoquer avec

poly .

la source
-2

find . -name \*.[ch] -print | xargs -n 1 wc -ldevrait faire l'affaire. Il existe également plusieurs variantes possibles, comme l'utilisation -execau lieu de canaliser la sortie vers wc.

John
la source
4
Mais find . -name \*.[ch] -printn'imprime pas le contenu des fichiers, seulement les noms de fichiers. Je compte donc le nombre de fichiers à la place, non? Ai-je besoin de `xargs '?
Niklas
@ Programmer400 oui, vous en aurez besoin xargs, et vous devrez également surveiller les wcappels multiples si vous avez beaucoup de fichiers; vous devez rechercher toutes les totallignes et les additionner.
Stephen Kitt
Si vous voulez juste le nombre total de lignes, vous devez le fairefind . -name \*.[ch] -print0 | xargs -0 cat | wc -l
moelleux
Notez que ceci ( find . -name \*.[ch] -print | wc -l) compte le nombre de fichiers (sauf si un nom de fichier contient une nouvelle ligne - mais c'est très inhabituel) - il ne compte pas le nombre de lignes dans les fichiers.
Jonathan Leffler