90

假设我想计算项目中的代码行数。如果所有文件都在同一个目录中,我可以执行:

cat * | wc -l

但是,如果有子目录,这是行不通的。为此, cat 必须具有递归模式。我怀疑这可能是 xargs 的工作,但我想知道是否有更优雅的解决方案?

4

11 回答 11

162

首先,您不需要使用cat来计算行数。这是一种称为无用使用 Cat (UUoC) 的反模式。要计算当前目录中文件的行数,请使用wc

wc -l * 

然后该find命令递归子目录:

find . -name "*.c" -exec wc -l {} \;
  • .是开始搜索的顶级目录的名称

  • -name "*.c"是您感兴趣的文件的模式

  • -exec给出要执行的命令

  • {}是要传递给命令的 find 命令的结果(这里wc-l

  • \;表示命令结束

此命令生成找到的所有文件及其行数的列表,如果您想获得所有找到的文件的总和,您可以使用 find 列出文件(使用-print选项),然后使用 xargs 将此列表作为参数传递到 wc-l。

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

编辑以解决 Robert Gamble 评论(谢谢):如果文件名中有空格或换行符(!),那么您必须使用-print0选项而不是-printandxargs -null以便文件名列表与以空字符结尾的字符串交换。

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

Unix 的哲学是拥有只做一件事的工具,并且把它做好。

于 2008-11-25T07:47:11.140 回答
30

如果你想要一个代码高尔夫的答案:

grep '' -R . | wc -l 

单独使用 wc -l 的问题是它不能很好地下降,并且使用的 oneliners

find . -exec wc -l {} \;

不会给你一个总行数,因为它为每个文件运行一次 wc,(大声笑!)和

find . -exec wc -l {} + 

一旦 find 达到参数的 ~200k 1 , 2 个 字符参数限制,而是多次调用 wc 每次只给你一个部分摘要,就会感到困惑。

此外,当遇到二进制文件时,上述 grep 技巧不会在输出中添加超过 1 行,这可能是有益的。

对于 1 个额外命令字符的成本,您可以完全忽略二进制文件:

 grep '' -IR . | wc -l

如果您也想对二进制文件运行行数

 grep '' -aR . | wc -l 
限制脚注:

文档对于它是字符串大小限制还是令牌数量限制有点含糊。

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

这意味着它很容易分块。

于 2008-11-25T08:02:20.283 回答
13

我想你可能被 xargs 困住了

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

chromakode的方法给出了相同的结果,但速度要慢得多。如果您使用 xargs 您的cat ing 和wc ing 可以在find开始查找时立即开始。

Linux上的好解释:xargs vs. exec {}

于 2008-11-25T07:42:29.527 回答
12

尝试使用find默认递归目录的命令:

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

于 2008-11-25T07:41:51.243 回答
10

正确的方法是:

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

您必须使用 -print0,因为 Unix 文件名中只有两个无效字符:空字节和“/”(斜杠)。因此,例如“xxx\npasswd”是一个有效的名称。但实际上,您更有可能遇到其中包含空格的名称。上面的命令会将每个单词计为一个单独的文件。

您可能还想使用“-type f”而不是-name 来限制对文件的搜索。

于 2008-11-25T08:02:25.273 回答
8

如果您可以使用相对较新的 GNU 工具,包括 Bash,则在上述解决方案中使用 cat 或 grep 是一种浪费:

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

这处理带有空格、任意递归和任意数量的匹配文件的文件名,即使它们超过了命令行长度限制。

于 2009-06-09T06:07:42.480 回答
2

我喜欢在项目目录中的所有文件上使用findhead来“递归猫”,例如:

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

优点是 head 会添加你的文件名和路径:

==> ./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 

有关此处的完整示例,请参阅我的博客文章:

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

注意我使用了'head -10000',显然如果我有超过 10,000 行的文件,这将截断输出......但是我可以使用 head 100000 但对于“非正式项目/目录浏览”,这种方法对我来说非常有效。

于 2013-04-28T11:10:07.990 回答
2
wc -cl `find . -name "*.php" -type f`
于 2014-08-23T09:58:08.227 回答
1

如果您只想生成总行数而不是每个文件的行数,例如:

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

效果很好。这使您无需在脚本中进行进一步的文本过滤。

于 2011-04-10T16:42:03.043 回答
0

这是一个计算项目中代码行数的 Bash 脚本。它递归地遍历源代码树,并排除使用“//”的空行和单行注释。

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

这是我的项目的输出:

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

享受!——柯伦

于 2014-03-31T23:58:48.600 回答
0
find . -name "*.h" -print | xargs wc -l
于 2014-05-27T13:02:05.043 回答