如果我想计算一个项目中的代码行数。如果所有的文件都在同一个目录中,我可以执行:
cat * | wc -l
但是,如果存在子目录,则此方法无效。为了使其有效,cat命令需要具有递归模式。我猜这可能是xargs的工作,但我想知道是否有更优雅的解决方案?
首先,您无需使用cat
命令来计算行数。这是一种名为“Useless Use of Cat”(UUoC)的反模式。要计算当前目录中文件的行数,请使用wc
命令:
wc -l *
然后find
命令会递归子目录:
find . -name "*.c" -exec wc -l {} \;
.
是要从顶级目录开始搜索的名称。
-name "*.c"
是您感兴趣的文件的模式。
-exec
给出要执行的命令。
{}
是要传递给命令 (这里是 wc-l
) 的查找命令的结果。
\;
表示命令结束。
该命令会列出所有找到的文件及其行数,如果您想要所有找到的文件的总行数,则可以使用 find 列出文件(使用 -print
选项),然后再使用 xargs 将此列表作为参数传递给 wc-l。
find . -name "*.c" -print | xargs wc -l
编辑以回应Robert Gamble的评论(感谢):如果文件名中有空格或换行符(!),则必须使用-print0
选项替代-print
和xargs -null
,以便文件名列表被交换为以null结尾的字符串。
find . -name "*.c" -print0 | xargs -0 wc -l
Unix哲学是拥有只做一件事情并且做好的工具。cat * | wc -l
这样的管道正是 cat
(代表连接)设计的精髓所在。"无用的 cat" 是指使用 cat
读取一个单一文件并将其通过管道传递给程序,而不是使用输入重定向。如果您不相信在这里使用 cat
不是无用功,只需尝试 wc -l *
和 cat * | wc -l
,观察它们的输出差异即可。 - user4815162342grep '' -R . | wc -l
find . -exec wc -l {} \;
不会给你总行数,因为它会对每个文件运行一次wc命令(哈哈!),
find . -exec wc -l {} +
当查找命中约200k字符时,xargs命令的参数限制会导致混乱,因此xargs会多次调用wc,每次只提供部分摘要。1,2
此外,上述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
-exec cmd {} +
对文件参数的上限为32000。 - sanmiguelcat **/* | wc -l
更短一些,而且它还会忽略隐藏文件和文件夹(例如 .git
中的文件),这可能是有益的。 - psmith我认为你可能需要使用xargs。
find -name '*php' | xargs cat | wc -l
chromakode的方法给出了相同的结果,但速度要慢很多。如果你使用xargs,你的cat和wc可以在find开始查找时就可以开始了。
关于Linux: xargs vs. exec {}有一个好的解释,请参考http://dmiessler.com/blog/linux-xargs-vs-exec
尝试使用find
命令,它默认递归目录:
find . -type f -execdir cat {} \; | wc -l
xargs
方法逃过了我。感谢你教给我一些东西! - chromakode正确的方式是:
find . -name "*.c" -print0 | xargs -0 cat | wc -l
你必须使用-print0,因为Unix文件名中只有两个无效字符:空字节和“/”(斜杠)。因此,例如“xxx \ npasswd”是一个有效的名称。实际上,你更可能遇到其中包含空格的名称。上述命令将把每个单词都计算为一个单独的文件。
您可能还想使用“-type f”而不是-name来限制搜索文件。
如果你使用相对较新的GNU工具,包括Bash,那么在上述解决方案中使用cat或grep是浪费的:
wc -l --files0-from=<(find . -name \*.c -print0)
这可以处理带有空格的文件名,任意递归和任何数量的匹配文件,即使它们超过了命令行长度限制。
wc -cl `find . -name "*.php" -type f`
我喜欢在项目目录中使用find和head命令一起进行“递归式查找”,例如:
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-including-headers/
请注意,我使用了“head -10000”,如果文件超过10,000行,则会截断输出...但是,对于“非正式项目/目录浏览”,我可以使用head 100000,这种方法非常适合我。
注:原文中的代码未提供,因此无法翻译。head -n-0
命令,它将输出所有行。-n, --lines=[-]K
参数用于指定输出文件的前 K 行或除最后 K 行之外的所有行。当参数以负号开头时,表示输出除最后 K 行之外的所有行。 - Kent Fredricfind . -type f -exec wc -l {} \; | awk '{total += $1} END{print total}'
运作良好。这样可以省去在脚本中进一步进行文本过滤的需要。
find . -name "*.h" -print | xargs wc -l
find
会递归搜索当前路径下的所有.h文件,并将找到的文件列表通过“|”(管道)作为输入传递给xargs
。xargs
读取这些文件并将每行转换为空格分隔的参数,传递给wc
命令,实际上是对文件中的行数进行计数。 - SD.
sloccount
程序。 - Basile Starynkevitch