On a UNIX system, "locate" searches the database for files with chosen name or files within the folder with the chosen name. How can I use locate to output only folders, not files?
11 Answers
Actually, locate
has what it takes if you use the --regexp
option and you don't mind it spitting out files that have the same name as the directories you seek. The "end of line" position marker gets the job done:
locate -r '/dirname$'
locate
also supports --ignore-case
if that's what you want.
-
1
-
Mateen's approach doesn't filter for directories. It returns everything in the database. The OP was looking for a specific directory and seems to know the name of the directory he wants.– Frank MApr 3, 2018 at 21:33
-
1
-
@CodyChan: I was assuming "dirname" is the entire directory name. Can you show an example where this doesn't work?– Frank MFeb 10, 2021 at 22:38
-
string
is part of the directory name such as I need to search directory whose name containsdef
such as directory "abc_def_ghi" since most of the time I need to locate a directory containing a string, just like locating a file containing a word but it is a directory.– CodyChanFeb 12, 2021 at 17:54
locate
itself can't do it for you. So the UNIX way to do it is to filter the output of locate
:
locate --null something | xargs -r0 sh -c 'for i do [ -d "$i" ] && printf "%s\n" "$i"; done' sh {} +
-
2
-
The proper way is with something like
locate --null something | xargs -r0 sh -c 'for i do [ -d "$i" ] && printf "%s\n" "$i"; done' sh {} +
.{}
should never be embedded in the shell code, that's as bad as usingeval
with arbitrary data (hence the downvote).– schApr 2, 2015 at 9:05 -
1@sch: Well, if you don't understand the consequences and if you're in the habit of having files named
$(reboot)
on your computer, maybe you shouldn't run commands you find on the internet withsudo
;D But, thanks for providing a better version, feel free to edit or post it as an answer, so it doesn't get lost in comments. Apr 2, 2015 at 9:56 -
1@ArchStanton, drop the {} as well. See man xargs and man find. I've submitted an edit suggestion. It's pending revew atm– schFeb 6, 2023 at 10:51
-
1@ArchStanton, close, though that would be less efficient as that would run one
sh
per file. There's also the question ofxargs
overall exit status. Using properif
constructs and do aexit 255
ifprintf
fails soxargs
aborts as well may be preferable.– schFeb 6, 2023 at 17:29
Why not use the find command ?
find . -name YOUR_SEARCH_NAME -type d
-
9locate is faster and I don't need it to be up to date all the time for my purpose.– shrxApr 3, 2012 at 20:30
find
as suggested in Scott Wilson's answer is what I would have used. However, if you really need to use the locate DB, a hackish solution could be
sudo strings /var/lib/mlocate/mlocate.db | grep -E '^/.*dirname'
sudo
since the database is not directly readable by regular users.strings
to strip metadata (this makes you also find directories to which you don't have read permission, whichlocate
usually hinders)./var/lib/mlocate/mlocate.db
is the DB path on Ubuntu, apparently (as an example. Other distributions might have it in other places, e.g./var/lib/slocate/slocate.db
).grep -E
to enable regular expressions.^/.*dirname
will match all lines that start with a/
, which all directories in the DB happen to do, followed by any character a number of times, followed by your search word.
Positive sides of this solution:
- it is faster than
find
, - you can use all the bells and whistles of
grep
(or other favourite text processing tools).
Negative sides:
- the same as
locate
in general (DB must be updated), - you need root access.
Putting Oliver Salzburg's neat line into your .bashrc
:
# locate directories:
# -------------------
locd () {
locate -0 -b -A "$@" | xargs -0 -I {} bash -c '[ -d "{}" ] && echo "{}"'
}
then you can type locd something
everytime you want to locate just directories.
Some breakdown:
-0
provides better support for filenames containing spaces or newlines.-b
or--basename
matches only the last part of the path.-A
matches all inputs, so if you provide multiple arguments, all must be present.
-
For me, to handle spaces in directories better, I use the following:
locate -i --all $1 | xargs -I {} bash -c 'if [ -d "'"{}"'" ]; then echo "'"{}"'"; fi'
Nov 9, 2018 at 20:30 -
Use locate -0 and xargs -0 flags if you get the xargs unterminated quote error. Dec 25, 2019 at 1:53
I know this thread is a little old but mlocate's limitations always bothered me too. I finally created my own version of slocate/mlocate called blocate
that uses sqlite for indexing and lets you index anything you want using a find
command. Performs really well and manages parallel indexing+searching with sqlite in just about 30 lines of bash. GPL - feedback welcome.
https://github.com/jboero/blocate
Hope this helps someone. I built a personal archive website for my backups indexing with mlocate but I got tired of searches taking forever and I didn't want to build a full database solution around it. My searches have gone from ~10s or so down to about 300ms.
With recent versions of GNU find (4.9 or above), you can do something like:
locate -0 '*/dirname' | find -files0-from - -prune -type d
That is, pass the list of files from the locate cache to find
which further filters it by type.
Replace -type
with -xtype
to also report symlinks eventually resolving to files of type directory like [
's or [[...]]
's -d
does.
With zsh
, you can also do:
files=( ${(0)"$(locate -0 '*/dirname')"} )
dirs=( $^files(N/) )
Were we use the /
glob qualifier to do the filtering by type (replace with -/
to also include symlinks to directories). Or in one go:
files=( ${(0)^"$(locate -0 '*/dirname')"}(N/) )
print
them r
aw on 1
C
olumn with:
print -rC1 -- $files
For instance.
-
In my system (Fedora 37, GNU findutils 4.9.0) I can only use
-files0-from -
, without the equal sign. Feb 7, 2023 at 17:09 -
@ArchStanton, thanks. Fixed now. I guess I got confused with the
--files0-from=-
option of other GNU utilities like sort/wc/du.– schFeb 8, 2023 at 8:10
I went with this solution:
locate -i "$foldername" | while read line
do
if [[ -d "$line" && `echo ${line##*/} | tr [:upper:] [:lower:]` = *`echo $foldername | tr [:upper:] [:lower:]`* ]]; then
echo "$line"
fi
done
-
-
@user128063 It's a bit late but anyway… It trims the string up to the last
/
. In general,${var##pattern}
trims the longest match from the beginning in$var
. See wiki.bash-hackers.org/syntax/pe#substring_removal Feb 7, 2023 at 23:08
Place these as last lines or where ever it fits best for you.
gedit ~/.bashrc
#system only
slocate() { locate $@ | egrep -v ˆ/home ; }
#system directories only
dslocate() { for directory in `locate $@ | egrep -v ˆ/home`; do if [ -d "$directory" ]; then echo $directory; fi; done ; }
#whole system directories only
dlocate() { for directory in `locate $@`; do if [ -d "$directory" ]; then echo $directory; fi; done ; }
#local user's only
llocate() { locate $@ | egrep ˆ/home ; }
#local user's directories only
ldlocate() { for directory in `locate $@ | egrep ˆ/home`; do if [ -d "$directory" ]; then echo $directory; fi; done ; }
hope this helps, cheers
This answer is the inverse of this other. Use
file $(locate -r 'xfce4-keyboard-overlay$') | grep directory$ | awk -F: '{ $(NF--)=""; print }'
Explanation:
locate ...
finds all files and directoriesfile $(...)
appends a message at the end of each line with the type of filegrep ...
filters lines containingdirectory$
awk ...
removes whatfile ...
appended
A small variation on others
for f in $(locate /dirname | grep /dirname$ ) ; do if [ -f $f ] ; then ls -1 $f ; fi ; done
If you want to match a pattern instead of providing the full name, there are also variations with grep
.