如何查找所有包含源代码的目录?

3

我有一个包含几个目录的项目(其中并非所有目录都是预先知道的)。我想要发出一个命令来查找所有包含源代码的目录。类似于 find . -name "*.cpp" 这将给我一个源码列表,而我只想要包含它们的目录列表。项目结构事先不知道,一些源代码可能存在于目录X中,而其他源代码可能存在于子目录X/Y中。哪个命令会打印出包含源代码的所有目录列表?


1
顺便说一句:很高兴看到你花时间回复了每一个回答者。 - mklement0
为什么你要问这个问题?那是什么项目?你想要一个通用的解决方案吗?为什么你不能使用项目源代码版本控制库?请编辑你的问题以改进它! - Basile Starynkevitch
6个回答

5
find . -name "*.cpp" -exec dirname {} \; | sort -u

如果(a)您有GNU find或最新版本的BSD find,并且(b)您有一个最新版本的dirname(例如GNU coreutils 8.21或FreeBSD 10,但不包括OSX 10.10),那么为了提高效率,请使用以下方法(致谢:Jochen和mklement0):
find . -name "*.cpp" -exec dirname {} + | sort -u

为什么我自己没想到呢? :)如果你有GNU find,你可以使用+而不是;轻松地节省对dirname的调用find . -name \*.cpp -exec dirname '{}' + | sort -u - Jochen
1
@Jochen 谢谢!另外,有趣的建议。你使用哪个 dirname 处理多个参数?我的(Debian Stable)不支持。 - John1024
@John1024:dirname(GNU coreutils) 8.13版本(例如,在Debian 3.2上)不支持多个参数,但是(GNU coreutils) 8.21版本(例如,在Ubuntu 14.04和Fedora 20上)支持。同样,在BSD世界中,OSX 10.10版本不支持多个参数,而FreeBSD 10版本支持。 - mklement0
1
@mklement0和Jochen,谢谢!答案已更新,包括+选项和版本信息。 - John1024
很高兴为您服务,@John1024。我认为这里只有dirname版本是变量,而不是find版本,因为以+结尾的-exec符合POSIX标准的功能 - mklement0

3

John1024的答案非常简洁明了,而且速度很快,如果你的版本的dirname支持多个参数,并且可以使用-exec dirname {} +调用它。

否则,使用-exec dirname {} \;,每个输入文件名都会fork出一个子进程,这样会非常慢。

如果:

  • 你的dirname不支持多个参数
  • 并且性能很重要
  • 并且你正在使用bash 4或更高版本

请考虑以下解决方案:

shopt -s globstar; printf '%s\n' ./**/*.cpp | sed 's|/[^/]*$||' | sort -u
  • shopt -s globstar 激活跨目录路径名扩展(globbing)的支持。
  • ./**/**.cpp 然后匹配当前目录子树中任何位置的.cpp文件。

    • 请注意,通配符故意以./开头,因此下面的sed命令也可以正确报告顶级目录本身,如果它包含匹配的文件。
  • sed 's|/[^/]*$||' 实际上执行与dirname相同的操作,但是在单个 sed调用中对所有输入行进行操作。

  • sort -u 对结果进行排序,并仅输出唯一的目录名称。

1
Globstar是一个被低估的功能:+1。 - John1024

1
find . -name "*.cpp" | while read f; do dirname "$f" ; done | sort -u

应该做你所需的。

1
你说得没错,但是@John1024写了一个更短、更简洁的命令。 - e271p314

1
find . -name '*.cpp' | sed -e 's/\/[^/]*$//' | sort | uniq

1
你做对了,但是@John1024写了一个更短、更简洁的命令。 - e271p314

1

要查找非空目录:

$ find . \! -empty -type d

如果只想查找特定文件类型的目录,可以使用以下命令:

find . -name \*.cpp | while read line; do dirname "${line}" ; done | sort -u

此命令会查找所有 *.cpp 文件,并对每个文件名调用 dirname。然后对结果进行排序并去重。使用 shell 内置命令可能有更快的方法,不需要为每个 *.cpp 文件生成新进程。但对于大多数项目来说,这可能并不重要。


1
find中使用shell内置命令不是一个选项,但如果您使用的是bash 4+,则可以使用shopt -s globstar和路径名扩展来实现更快的解决方案。 - mklement0

0

你应该定义什么是源文件。

请注意,某些 C 或 C++ 文件是生成的(例如通过解析器生成器如bisonyacc、由特定于项目的生成器等生成),而一些包含的 C 或 C++ 文件未命名为.h.cc(请阅读有关X-macros的内容)。在GCC中,大量的文件是生成的(例如从*.md机器描述文件生成的文件,这些是真正的源文件)

大多数大型软件项目(例如由许多百万行 C++ 或 C 代码组成的项目)都具有或正在使用某些 C 或 C++ 代码生成器。

在自由软件世界中,源代码只是开发人员正在使用的代码的首选形式

请注意,源代码可能不会放在文件中;它可以放在数据库中,或者是堆镜像中,例如如果开发人员正在与特定程序交互以进行工作(请记住1980年代的Smalltalk机器,或INRIA在1980年的Mentor structured editor)。另一个例子是J.Pitrat的CAIA系统完全是自动生成的C代码。还请查看Scheme48

也许(仅作为近似启发式方法),您应该将任何命名为.h.cc.cpp.cxx或者可能包含GENERATED FILE单词(通常在某些注释中)但不包含在内的.def.inc.tcc文件视为C++源文件。

要了解生成的文件是什么,您应该深入了解构建过程(由MakefileCMake*Makefile.amautoconf等描述...)。 没有一种万无一失的方法来检测或猜测生成的C++文件,因此您将无法可靠地自动化检测它们。
最后,引导式语言通常具有包含某些生成文件的(版本控制)存储库。Ocaml具有boot/子目录,而MELT具有一个melt/generated/目录(其中包含生成MELT的C++文件*.melt源代码文件)以C++形式。

我建议使用项目版本控制存储库并获取那里的非空目录。具体细节取决于版本控制工具(例如git,或svn,或hg等)。您应该使用一些版本控制(或修订控制工具。我推荐使用git


信息不错,但你可能想多了:我认为 OP 不是在寻找一种通用的方法来检测 [C++] 源文件。他们接受的答案表明,只需要找到包含感兴趣后缀名文件的目录即可。 - mklement0
那么我相信我的最后一段是相关的。原帖作者应该使用版本控制工具。 - Basile Starynkevitch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接