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?
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.
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.
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 {}
Essayez d'utiliser la commande find
, qui récursive les répertoires par défaut:
find . -type f -execdir cat {} \; | wc -l
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.
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.
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.
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.
wc -cl `find . -name "*.php" -type f`
find . -name "*.h" -print | xargs wc -l
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