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.
- 279
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 {} +
- 89,072
- 65
- 269
- 311
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.
- 169
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 raw on 1 Column with:
print -rC1 -- $files
For instance.
- 367
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'
sudosince the database is not directly readable by regular users.stringsto strip metadata (this makes you also find directories to which you don't have read permission, whichlocateusually hinders)./var/lib/mlocate/mlocate.dbis 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 -Eto enable regular expressions.^/.*dirnamewill 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
locatein general (DB must be updated), - you need root access.
- 24,865
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:
-0provides better support for filenames containing spaces or newlines.-bor--basenamematches only the last part of the path.-Amatches all inputs, so if you provide multiple arguments, all must be present.
- 1,795
- 425
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
- 474
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.
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
- 1,445