web-dev-qa-db-fra.com

Comment compter les lignes de code, y compris les sous-répertoires

Supposons que je veuille compter les lignes de code d'un projet. Si tous les fichiers sont dans le même répertoire, je peux exécuter:

cat * | wc -l

Cependant, s'il existe des sous-répertoires, cela ne fonctionne pas. Pour que cela fonctionne, le chat devrait avoir un mode récursif. Je soupçonne que cela pourrait être un travail pour xargs, mais je me demande s'il existe une solution plus élégante?

88
speciousfool

D'abord, vous n'avez pas besoin d'utiliser cat pour compter les lignes. Il s'agit d'un anti-modèle appelé Useless Use of Cat (UUoC). Pour compter les lignes dans les fichiers du répertoire courant, utilisez wc :

wc -l * 

Ensuite, la commande find récursive les sous-répertoires:

find . -name "*.c" -exec wc -l {} \;
  • . est le nom du premier répertoire à partir duquel lancer la recherche

  • -name "*.c" est le modèle du fichier qui vous intéresse

  • -exec donne une commande à exécuter

  • {} est le résultat de la commande find à passer à la commande (ici wc-l)

  • \; indique la fin de la commande

Cette commande produit une liste de tous les fichiers trouvés avec leur nombre de lignes, si vous voulez avoir la somme pour tous les fichiers trouvés, vous pouvez utiliser find to lister les fichiers (avec le -print option) et utiliser xargs pour passer cette liste en argument à wc-l.

find . -name "*.c" -print | xargs wc -l 

EDIT pour répondre au commentaire de Robert Gamble (merci): si vous avez des espaces ou des retours à la ligne (!) Dans les noms de fichiers, vous devez utiliser -print0 option au lieu de -print et xargs -null pour que la liste des noms de fichiers soit échangée avec des chaînes terminées par null.

find . -name "*.c" -print0 | xargs -0 wc -l

La philosophie Unix est d'avoir des outils qui ne font qu'une chose et le font bien.

154
philant

Si vous voulez une réponse par code-golf:

grep '' -R . | wc -l 

Le problème avec l'utilisation de wc -l seul est qu'il ne peut pas bien descendre, et les oneliners utilisant

find . -exec wc -l {} \;

Ne vous donnera pas un nombre total de lignes car il exécute wc une fois pour chaque fichier (loL!) Et

find . -exec wc -l {} + 

Sera confus dès que find atteint le ~ 200k 1,2 limite d'argument de caractères pour les paramètres et appelle à la place wc multiple fois, chaque fois ne vous donnant qu'un résumé partiel.

De plus, l'astuce grep ci-dessus n'ajoutera pas plus d'une ligne à la sortie lorsqu'elle rencontrera un fichier binaire, ce qui pourrait être indirectement bénéfique.

Pour le coût d'un caractère de commande supplémentaire, vous pouvez ignorer complètement les fichiers binaires:

 grep '' -IR . | wc -l

Si vous souhaitez également exécuter le décompte des lignes sur les fichiers binaires

 grep '' -aR . | wc -l 

La documentation est un peu vague quant à sa limite de taille a string ou à nombre de jetons.

cd /usr/include;
find -type f -exec Perl -e 'printf qq[%s => %s\n], scalar @ARGV, length join q[ ], @ARGV' {} + 
# 4066 => 130974
# 3399 => 130955
# 3155 => 130978
# 2762 => 130991
# 3923 => 130959
# 3642 => 130989
# 4145 => 130993
# 4382 => 130989
# 4406 => 130973
# 4190 => 131000
# 4603 => 130988
# 3060 => 95435

Cela implique qu'il va se fragmenter très très facilement.

30
Kent Fredric

Je pense que vous êtes probablement coincé avec des xargs

find -name '*php' | xargs cat | wc -l

La méthode de chromakode donne le même résultat mais est beaucoup plus lente. Si vous utilisez xargs, votre chat ing et wc ing peut démarrer dès comme find commence à trouver.

Bonne explication sur Linux: xargs vs exec {}

13
Ken

Essayez d'utiliser la commande find, qui récursive les répertoires par défaut:

find . -type f -execdir cat {} \; | wc -l

10
chromakode

La bonne façon est:

find . -name "*.c" -print0 | xargs -0 cat | wc -l

Vous devez utiliser -print0 car il n'y a que deux caractères non valides dans les noms de fichiers Unix: l'octet nul et "/" (barre oblique). Ainsi, par exemple, "xxx\npasswd" est un nom valide. En réalité, vous êtes plus susceptible de rencontrer des noms avec des espaces en eux. Les commandes ci-dessus compteraient chaque mot comme un fichier distinct.

Vous pouvez également utiliser "-type f" au lieu de -name pour limiter la recherche aux fichiers.

10
Aaron Digulla

Utiliser cat ou grep dans les solutions ci-dessus est un gaspillage si vous pouvez utiliser des outils relativement récents GNU, y compris Bash:

 wc -l --files0-from = <(find. -name\*. c -print0) 

Cela gère les noms de fichiers avec des espaces, une récursion arbitraire et n'importe quel nombre de fichiers correspondants, même s'ils dépassent la limite de longueur de ligne de commande.

8
Idelic

J'aime utiliser find et head ensemble pour "un chat récursif" sur tous les fichiers d'un répertoire de projet, par exemple:

find . -name "*rb" -print0 | xargs -0 head -10000

L'avantage est que head ajoutera le nom de fichier et le chemin:

==> ./recipes/default.rb <==
DOWNLOAD_DIR = '/tmp/downloads'
MYSQL_DOWNLOAD_URL = 'http://cdn.mysql.com/Downloads/MySQL-5.6/mysql-5.6.10-debian6.0-x86_64.deb'
MYSQL_DOWNLOAD_FILE = "#{DOWNLOAD_DIR}/mysql-5.6.10-debian6.0-x86_64.deb"

package "mysql-server-5.5"
...

==> ./templates/default/my.cnf.erb <==
#
# The MySQL database server configuration file.
#
...

==> ./templates/default/mysql56.sh.erb <==
PATH=/opt/mysql/server-5.6/bin:$PATH 

Pour l'exemple complet ici, veuillez consulter mon article de blog:

http://haildata.net/2013/04/using-cat-recursively-with-nicely-formatted-output-including-headers/

Remarque J'ai utilisé 'head -10000', clairement si j'ai des fichiers de plus de 10 000 lignes, cela va tronquer la sortie ... cependant je pourrais utiliser head 100000 mais pour la "navigation informelle dans les projets/répertoires", cette approche fonctionne très bien pour moi.

2
Dave Pitts

Si vous souhaitez générer uniquement un nombre total de lignes et non un nombre de lignes pour chaque fichier, quelque chose comme:

find . -type f -exec wc -l {} \; | awk '{total += $1} END{print total}'

fonctionne bien. Cela vous évite d'avoir à filtrer davantage le texte dans un script.

1
abalmos
wc -cl `find . -name "*.php" -type f`
1
PMD
find . -name "*.h" -print | xargs wc -l
0
SD.

Voici un script Bash qui compte les lignes de code d'un projet. Il parcourt récursivement une arborescence source et exclut les lignes vides et les commentaires sur une seule ligne qui utilisent "//".

# $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`
    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 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

0
curran