You’re working on a non-trivial project, with multiple source files, in multiple languages. You want to list all C/C++ source files in the project, recursively, but without getting duplicates from the build dir.
Assuming your C/C++ files are all .h or .cc files, and the build directory is build/ – how would you do it?
Here’s my preferred solution:
$ find . -name \*.h -or -name \*.cc | grep -vE "^\.\/build\/"
Shell-Foo credit for this one: Eyal Fink.
Shell-Foo is a series of fun ways to take advantage of the powers of the shell. In the series, I highlight shell one-liners that I found useful or interesting. Most of the entries should work on bash on Linux, OS X and other UNIX-variants. Some probably work with other shells as well. Your mileage may vary.
Feel free to suggest your own Shell-Foo one-liners!
Explanation
- find finds all files that match a pattern, recursively from a given directory.
- The first argument (
.
in my example) is the starting directory. - The
-name <pattern>
is a name pattern that matches the path relative to the starting directory.\*.h
is like*.h
– meaning anything that ends with “.h”. The*
needs to be escaped so it gets to the find program, avoiding premature expansion.
- The first argument (
- The find command writes to STDOUT all files that matched the pattern. This will include such files under the build directory.
- grep will filter out those matches under the build directory.
-v
means invert-grep – so only lines that don’t match the grep pattern are printed.-E
means that the pattern is a regular expression. The RegEx is “^./build/”, with.
and/
escaped. This pattern matches strings that begin (^
) with “./build/”.
Alternate solutions
An important downside of the solution I presented is performance. If the build directory is deep and has many C/C++ files, find will spend time traversing it, only to have grep spending time filtering it…
It’s possible to tell find to “prune” the build directory, so it skips it altogether!
$ find . -path ./build -prune -name '*.cc' -or -name '*.h'
I dislike this version, because it confuses me that -prune
appears after the pruned pattern… Although this is less typing compared to the other way, so maybe it worths getting used to.