Just to show that you can do this without having to enlist the help of find
. You can make use of grep
's ability to recurse the filesystem too. Assuming your version of grep
has the -R
switch:
$ grep -R version | awk -F: '/CMake.*:/ {print $1}'
Example
I created some sample data which included files named CMake1-3 which contained the string version
, as well as a file named CMake0 which did not. I also created 2 files named afile
that also contained the string version
.
$ tree .
.
|-- CMake1
|-- dir1
| |-- dirA1
| | `-- CMake1
| |-- dirA2
| `-- dirA3
|-- dir2
| |-- dirA1
| | `-- CMake0
| |-- dirA2
| | |-- afile
| | `-- CMake2
| `-- dirA3
`-- dir3
|-- dirA1
|-- dirA2
| `-- afile
`-- dirA3
`-- CMake3
Now when I run the above command:
$ grep -R version | awk -F: '/CMake.*:/ {print $1}'
CMake1
dir2/dirA2/CMake2
dir3/dirA3/CMake3
dir1/dirA1/CMake1
Details
The above commands produces a list like this from grep
:
$ grep -R version
CMake1:version
dir2/dirA2/CMake2:version
dir2/dirA2/afile:version
dir3/dirA2/afile:version
dir3/dirA3/CMake3:version
dir1/dirA1/CMake1:version
And awk
is used to find all the strings that contain CMake.*:
and to split these strings on a colon (:
), and to return just the first field from this split, i.e., the path of the name of a corresponding CMake*
file.
2 grep's + PCRE
More modern versions of grep will often include what's called PCRE - Perl Compatible Regular Exprssions. So you could use 2 grep
commands, with the 2nd one using PCRE to extract just the path portion of the file, omitting the trailing :version
bit of the string from the 1st grep
.
Example
$ grep -R version | grep -Po '.*CMake.*(?=:version)'
CMake1
dir2/dirA2/CMake2
dir3/dirA3/CMake3
dir1/dirA1/CMake1
The -o
will return just the matching portion, while -P
is what enables PCRE. Within the regular expression we're using a lookahead, (?=...
), at the end to select only strings that have a trailing :version
. This lookahead is only to help align our pattern, it isn't included in the results we return, nor is it part of the actual pattern we're grepping for, i.e. CMake.*
.
Line numbers
You can also include the switch -n
to the first grep
so that the line number where the string version
was encountered can also be included in the output.
Example
$ grep -Rn version | grep -Po '.*CMake.*(?=:version)'
CMake1:1
dir2/dirA2/CMake2:9
dir3/dirA3/CMake3:1
dir1/dirA1/CMake1:1
To make the first example work you'd need to change the awk
commands slightly:
$ grep -Rn version | awk -F: '/CMake.*:/ {print $1":"$2}'
CMake1:1
dir2/dirA2/CMake2:9
dir3/dirA3/CMake3:1
dir1/dirA1/CMake1:1
The first example gives you the opportunity though to move the line number around, since we've already parsed it into a separate field via awk
.
Example
Here we can put the number first.
$ grep -Rn version | awk -F: '/CMake.*:/ {print $2":"$1}'
1:CMake1
9:dir2/dirA2/CMake2
1:dir3/dirA3/CMake3
1:dir1/dirA1/CMake1