@Cas van Tijn - I think you are slightly misunderstanding what grep does.
Grep filters entire lines of text. And its behavior is to be expected. It's doing exactly what you tell it to do.
When you use
grep
, it only reports entire lines of text containing the specified pattern.
And if you use
grep -v
, or
grep --invert-match
it only reports lines that DO NOT match the specified pattern!
So where you have used:
Bash:
locate -br '^test.sh$' | grep -v test.sh
The following happens:
1. The
locate
command will find any instances of files named
test.sh
and then those results are piped to your
grep
command. So we'd expect ALL lines to contain test.sh.
2. Because you have used
grep -v
it will only report lines of text that DO NOT contain the pattern "test.sh".
3. Because ALL of the results from
locate
contain "test.sh" (because that's what you're looking for) - grep reports nothing - because once again - you used the
-v
option, telling it to only report lines that DO NOT contain "test.sh".
Does that makes sense?!
So how do we do what you want?
Well, if you want to use the
locate
command to find all instances of files with a particular file-name and then report only the path to the directory containing the files, you should perhaps consider using
dirname
.
So you could do something like this:
Bash:
locate -br '^test.sh$' | while read -r line ; do dirname "$line" ; done
That will list all directories which contain a file which exactly matches "test.sh".
The
locate
command finds the paths for all files which exactly matches "test.sh" and pipes it to a
while....read
loop, which then calls dirname on each returned line, to report the name of the directory containing a file called "test.sh".
OR, if you're going to use this kind of thing a lot, with different search patterns, you could make it a little more generic and add a function to your ~/.bashrc like this:
Bash:
function dirs_with
{
if [ $# -ne 1 ] ; then
echo "Error: requires a single parameter - a regex for a file"
echo "e.g."
echo " dirs_with '\.mp3$'"
echo "To find all directories containing files ending in .mp3"
echo "or"
echo " dirs_with '^test.sh$'"
echo "To find all directories containing files called 'test.sh'"
return 1
fi
locate -br "$1" |
while read -r line ; do
dirname "$line"
done | sort -u
}
The above function expects to receive a single parameter - a regex for a file-pattern.
If it receives the wrong number of parameters, it displays an error/usage message and quits.
The rest of the code is pretty much the same as before.
We use the passed-in parameter as the regex to use with the
locate
command. The results from
locate
are piped to a
while...read
loop which runs
dirname
on each result, to report the path to the directory containing each file.
And the final
sort
, sorts the results alphabetically and because we've used the
-u
option, it sorts uniquely and removes any duplicates.
This is in case you used a regex like '\.mp3$' which matches ALL files that end in ".mp3" - which in turn could return multiple results for a single directory. So by sorting them uniquely - any directories with multiple .mp3 files in them will only be reported once!
So now you should be able to feed the function pretty much any valid regex and it should report any directories containing files with that regex.
With the function in your .bashrc, it will be available in any new terminal sessions you open.
To make it available in the current terminal (or any other terminals that were open before you added the function to your .bashrc), run the command:
And that will re-read your .bashrc and will make the function available to you.
So for your particular usage, you'd run the function like this:
And if you wanted to identify ALL directories containing .txt files:
Whether you just go with the one-liner, or try adding the function to your .bashrc - it's up to you.
Going back to your original problem - the problem wasn't with
grep
. It did exactly what you told it to do! The real problem was simply that you were using the wrong tool for the job!
I hope this helps. If you have any questions, or concerns - fire away!
EDIT:
One caveat to using
locate
is that it relies on a database, which is updated via the
updatedb
command. Some distros periodically run the
updatedb
command automatically. But the database is
NOT always guaranteed to be completely up to date.
If you're searching for things that have been on your PC for a while and the database isn't too far out of date - it's a pretty fast way of searching for things.
But if you're searching for something that was recently added - you may have to manually run the
updatedb
command in order to refresh the database before using
locate
.
If you run
locate
on an outdated database, it will report files that may have already been deleted, and will not report newer files that haven't been indexed yet.