compter les lignes de code (non vides) dans bash

151

Dans Bash, comment compter le nombre de lignes de code non vierges dans un projet?

Jonathan Hartley
la source
1
La plupart des solutions ci-dessous ne fonctionnent que pour un seul fichier (par exemple foo.c). Avez-vous des réflexions sur le nombre total de lignes dans un projet (par exemple, de nombreux fichiers dans la structure de répertoires, et à l'exclusion des fichiers binaires)?
resolutionPuzzles
5
@solvingPuzzles Je pense que je peux répondre à cette partie. Pour toute solution qui fonctionne sur un fichier, par exemple "cat FILE | sed blah", vous pouvez travailler sur de nombreux fichiers en remplaçant le "cat FILE" par une commande qui répertorie les noms de fichiers sur lesquels opérer, par exemple "find. -Name '* .py '", et dirigez-le dans" xargs cat ". par exemple "find. -name '* .py' | xargs cat | sed '/ ^ \ s * $ / d' | wc -l"
Jonathan Hartley
2
@JonathanHartley @solvingPuzzles il y a aussi des programmes comme slocet clocqui sont là pour faire ces comptes de lignes de code.
AsTeR
OP ici: Quand j'ai posé ce problème pour la première fois, 'cloc' n'a pas fait un très bon travail sur le code Python. Aujourd'hui, c'est génial.
Jonathan Hartley
cloc est également disponible en tant que module npm et permet de gagner beaucoup de temps.
Krishna Vedula

Réponses:

193
cat foo.c | sed '/^\s*$/d' | wc -l

Et si vous considérez les commentaires comme des lignes vides:

cat foo.pl | sed '/^\s*#/d;/^\s*$/d' | wc -l

Bien que cela dépende de la langue.

Michael Cramer
la source
24
Je ne sais pas pourquoi vous utilisez le chat là-bas. Utilisez foo.c ou foo.pl comme nom de fichier à passer à sed. sed '/ ^ \ s * $ / d' foo.c | wc -l
Andy Lester
28
Juste une habitude. Je lis les pipelines de gauche à droite, ce qui signifie que je commence généralement par cat, puis action, action, action, etc. Clairement, le résultat final est le même.
Michael Cramer
32
Pour faire cela pour tous les fichiers de tous les sous-dossiers et pour exclure les commentaires avec '//', étendez cette commande dans ceci: find. -type f -nom '* .c' -exec cat {} \; | sed '/ ^ \ s * # / d; / ^ \ s * $ / d; / ^ \ s * \ / \ // d' | wc -l
Benjamin Intal
11
Vous pouvez lire de gauche à droite sans UUOC: < foo.pl sed 'stuff' | wc -l.
jw013
22
D'une manière générale, UUOC n'est pas important, mais la lisibilité l'est.
andersand
52
#!/bin/bash
find . -path './pma' -prune -o -path './blog' -prune -o -path './punbb' -prune -o -path './js/3rdparty' -prune -o -print | egrep '\.php|\.as|\.sql|\.css|\.js' | grep -v '\.svn' | xargs cat | sed '/^\s*$/d' | wc -l

Ce qui précède vous donnera le nombre total de lignes de code (lignes vierges supprimées) pour un projet (dossier actuel et tous les sous-dossiers de manière récursive).

Dans les fichiers ci-dessus, "./blog" "./punbb" "./js/3rdparty" et "./pma" sont des dossiers que je liste noire car je n'y ai pas écrit le code. Aussi .php, .as, .sql, .css, .js sont les extensions des fichiers examinés. Tous les fichiers avec une extension différente sont ignorés.

Gilles
la source
1
variante pour une application Rails: trouver. -path './log' -prune -o -path './trunk' -prune -o -path './branches' -prune -o -path './vendor' -prune -o -path './tmp '-prune -o -print | egrep '\ .rb | \ .erb | \ .css | \ .js | \ .yml' | grep -v 'svn' | chat xargs | sed '/ ^ \ s * $ / d' | wc -l
poseid
1
Vous devez ajouter un $à grep ( ...\.js$|...) sinon il correspondra feature.js.swp.
Xeoncross
Vous avez oublié l'ancrage, il contient donc de mauvais fichiers. Et une version encore plus simple avec ancrage:find . | egrep '.\.c$|.\.h$' | xargs cat | sed '/^\s*$/d' | wc -l
Mark Jeronimus
36

Si vous souhaitez utiliser autre chose qu'un script shell, essayez CLOC :

cloc compte les lignes vierges, les lignes de commentaires et les lignes physiques du code source dans de nombreux langages de programmation. Il est entièrement écrit en Perl sans dépendances en dehors de la distribution standard de Perl v5.6 et supérieur (le code de certains modules externes est intégré dans cloc) et est donc assez portable.

xsl
la source
2
Quand j'ai posé cette question pour la première fois, «cloc» comptait les docstrings Python comme des lignes de code, ce qui était à mon humble avis sous-optimal. Les versions modernes de 'cloc' comptent désormais les docstrings Python comme commentaires, ce que j'aime beaucoup plus.
Jonathan Hartley
C'est la bonne réponse! J'ai juste essayé cloc et ça fait bien le travail.
LeeMobile
31

Il existe de nombreuses façons de faire cela, en utilisant les utilitaires shell courants.

Ma solution est:

grep -cve '^\s*$' <file>

Ceci recherche les lignes dans <file> les lignes ne correspondent pas (-v) qui correspondent au modèle (-e) '^ \ s * $', qui est le début d'une ligne, suivi de 0 ou plusieurs caractères d'espacement, suivis à la fin d'une ligne (c'est-à-dire aucun contenu autre que les espaces), et afficher un nombre de lignes correspondantes (-c) au lieu des lignes correspondantes elles-mêmes.

Un avantage de cette méthode par rapport aux méthodes qui impliquent une connexion wc, est que vous pouvez spécifier plusieurs fichiers et obtenir un décompte distinct pour chaque fichier:

$ grep -cve '^\s*$' *.hh

config.hh:36
exceptions.hh:48
layer.hh:52
main.hh:39
CuillèreMeiser
la source
2
Merci! Incidemment, wc fournit un décompte pour chaque fichier donné, plus un total.
Jonathan Hartley
1
Mais pas si vous y accédez, car la norme ne compte qu'un seul fichier.
SpoonMeiser
C'est la meilleure réponse à mon avis.
simhumileco
-en'est pas nécessaire. C'est l'emplacement normal du motif et vous ne faites rien de funky avec. Mais rien de mal à être explicite, si c'est votre style.
Jacktose
13

'wc' compte les lignes, les mots, les caractères, donc pour compter toutes les lignes (y compris les lignes vides), utilisez:

wc *.py

Pour filtrer les lignes vides, vous pouvez utiliser grep:

grep -v '^\s*$' *.py | wc

'-v' dit à grep de sortir toutes les lignes sauf celles qui correspondent à '^' est le début d'une ligne '\ s *' est zéro ou plusieurs caractères d'espacement '$' est la fin d'une ligne * .py est mon exemple pour tous les fichiers que vous souhaitez compter (tous les fichiers python dans le répertoire courant) redirigent vers wc. Allez-y.

Je réponds à ma propre question (authentique). Impossible de trouver une entrée stackoverflow qui couvre ce problème.

Jonathan Hartley
la source
5
\ W ne correspond pas aux espaces, il correspond aux caractères autres que des mots. C'est l'opposé de \ w, caractères de mot. \ W correspondra à tout ce qui n'est pas alphanumérique ou souligné et ne fera donc pas ce que vous prétendez faire ici. Vous voulez dire \ s
SpoonMeiser
9

Cette commande compte le nombre de lignes non vides.
cat fileName | grep -v ^$ | wc -l
grep -v ^ $ fonction d'expression régulière ignore les lignes vides.

littoral
la source
Cette réponse est la plus simple
samthebest
2
Il n'y a pas besoin de catdans cette chaîne:grep -v ^$ fileName | wl -l
Aethalides
7
Il n'y a pas non plus besoin de wc -lcar grep a -c:grep -vc ^$ fileName
Jacktose
6
cat file.txt | awk 'NF' | wc -l
Jaydillan
la source
j'adore la simplicité de celui-ci 👏🏼
Gerard
5
cat 'filename' | grep '[^ ]' | wc -l

devrait faire l'affaire très bien

ridicule
la source
3
Pourquoi utiliser cat et diriger le fichier vers grep, alors que vous pouvez passer le nom de fichier comme argument à grep en premier lieu?
SpoonMeiser le
vrai, c'est juste un ancien alias que j'ai autour ... il fait essentiellement la même chose que votre solution au lieu d'utiliser l'inverse
curtisk
4
awk '/^[[:space:]]*$/ {++x} END {print x}' "$testfile"
Ben Hoffstein
la source
1
Je voterais simplement parce que je n'ai littéralement jamais vu personne utiliser le pré-incrément dans un script awk, mais malheureusement, cela ne compte que les lignes vides. :) Vous voulez dire awk '!/^[[:space:]]*$/{++x} END{print x}'. Ou, si vous détestez vraiment les négatifs awk '{y++} /^[[:space:]]*$/{++x} END{print y-x}'
,;
4
grep -cvE '(^\s*[/*])|(^\s*$)' foo

-c = count
-v = exclude
-E = extended regex
'(comment lines) OR (empty lines)'
where
^    = beginning of the line
\s   = whitespace
*    = any number of previous characters or none
[/*] = either / or *
|    = OR
$    = end of the line

Je poste ceci parce que d'autres options m'ont donné de mauvaises réponses. Cela a fonctionné avec ma source java, où les lignes de commentaires commencent par / ou * (j'utilise * sur chaque ligne dans les commentaires sur plusieurs lignes).

sami
la source
C'est une solution viable. Seule chose à noter: il ne compte pas les commentaires sur plusieurs lignes
Amol
2

Voici un script Bash qui compte les lignes de code dans un projet. Il parcourt une arborescence source de manière récursive et exclut les lignes vides et les commentaires sur une seule ligne utilisant "//".

# $excluded is a regex for paths to exclude from line counting
excluded="spec\|node_modules\|README\|lib\|docs\|csv\|XLS\|json\|png"

countLines(){
  # $total is the total lines of code counted
  total=0
  # -mindepth exclues the current directory (".")
  for file in `find . -mindepth 1 -name "*.*" |grep -v "$excluded"`; do
    # First sed: only count lines of code that are not commented with //
    # Second sed: don't count blank lines
    # $numLines is the lines of code
    numLines=`cat $file | sed '/\/\//d' | sed '/^\s*$/d' | wc -l`

    # To exclude only blank lines and count comment lines, uncomment this:
    #numLines=`cat $file | sed '/^\s*$/d' | wc -l`

    total=$(($total + $numLines))
    echo "  " $numLines $file
  done
  echo "  " $total in total
}

echo Source code files:
countLines
echo Unit tests:
cd spec
countLines

Voici à quoi ressemble la sortie de mon projet :

Source code files:
   2 ./buildDocs.sh
   24 ./countLines.sh
   15 ./css/dashboard.css
   53 ./data/un_population/provenance/preprocess.js
   19 ./index.html
   5 ./server/server.js
   2 ./server/startServer.sh
   24 ./SpecRunner.html
   34 ./src/computeLayout.js
   60 ./src/configDiff.js
   18 ./src/dashboardMirror.js
   37 ./src/dashboardScaffold.js
   14 ./src/data.js
   68 ./src/dummyVis.js
   27 ./src/layout.js
   28 ./src/links.js
   5 ./src/main.js
   52 ./src/processActions.js
   86 ./src/timeline.js
   73 ./src/udc.js
   18 ./src/wire.js
   664 in total
Unit tests:
   230 ./ComputeLayoutSpec.js
   134 ./ConfigDiffSpec.js
   134 ./ProcessActionsSpec.js
   84 ./UDCSpec.js
   149 ./WireSpec.js
   731 in total

Prendre plaisir! - Curran

Curran
la source
1

Cela dépendra un peu du nombre de fichiers que vous avez dans le projet. En théorie, vous pourriez utiliser

grep -c '.' <list of files>

Où vous pouvez remplir la liste des fichiers à l'aide de l'utilitaire de recherche.

grep -c '.' `find -type f`

Vous donnerait un nombre de lignes par fichier.

Linor
la source
1
. correspond aux espaces. Cette solution ne fonctionne que si vous considérez qu'une ligne contenant uniquement des espaces blancs n'est pas vide, ce qui est techniquement le cas, même si ce n'est probablement pas ce que vous recherchez.
SpoonMeiser
1

Script pour compter récursivement toutes les lignes non vides avec une certaine extension de fichier dans le répertoire courant:

#!/usr/bin/env bash
(
echo 0;
for ext in "$@"; do
    for i in $(find . -name "*$ext"); do
        sed '/^\s*$/d' $i | wc -l ## skip blank lines
        #cat $i | wc -l; ## count all lines
        echo +;
    done
done
echo p q;
) | dc;

Exemple d'utilisation:

./countlines.sh .py .java .html
Keith Pinson
la source
Merci à @Andy Lester (+1 sur votre commentaire) pour la partie "non vierge" de la recette.
Keith Pinson
Merci également à @Michael Cramer (+1 sur votre message) pour avoir initialement publié la solution (un peu plus verbeuse) "non vide".
Keith Pinson
1

Si vous voulez la somme de toutes les lignes non vierges pour tous les fichiers d'une extension de fichier donnée dans un projet:

while read line
do grep -cve '^\s*$' "$line"
done <  <(find $1 -name "*.$2" -print) | awk '{s+=$1} END {print s}'

Le premier argument est le répertoire de base du projet, le second est l'extension du fichier. Exemple d'utilisation:

./scriptname ~/Dropbox/project/src java

Ce n'est guère plus qu'une collection de solutions précédentes.

Andy
la source
Celui-ci obtient le prix du plus grand nombre d'appels fork + exec en lançant grep une fois par ligne dans chaque fichier. ;)
dannysauer
0
grep -v '^\W*$' `find -type f` | grep -c '.' > /path/to/lineCountFile.txt

donne un décompte global pour tous les fichiers du répertoire courant et de ses sous-répertoires.

HTH!

néerlandais
la source
\ W est des caractères non mot; cela ne correspondra pas à une ligne comme ${-[*]} + $@, par exemple. Ce qui est sûrement un code valide quelque part dans le monde. ;) Vous voulez dire \ s pour l'espace.
dannysauer
0

Cela donne le nombre de lignes sans compter les lignes vides:

grep -v ^$ filename wc -l | sed -e 's/ //g' 
mahesh
la source
0
rgrep . | wc -l

donne le nombre de lignes non vides dans le répertoire de travail courant.

jean-emmanuel
la source
-3

Il existe déjà un programme pour cela sur Linux appelé «wc».

Juste

wc -l *.c 

et il vous donne le nombre total de lignes et les lignes pour chaque fichier.

G1i1ch
la source
3
Hey. 'wc' en lui-même ne recherche pas les sous-répertoires, et il ne filtre pas les lignes vides, tous deux explicitement demandés dans la question.
Jonathan Hartley
wccompte les lignes vides. L'OP veut compter les lignes non vierges. Il est vrai qu'il voudra l'utiliser wc, mais seulement après avoir été édité en streaming à l'aide desed
EhevuTov