16

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?

shrx
  • 474

11 Answers11

17

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.

Frank M
  • 279
4

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 {} +
Oliver Salzburg
  • 89,072
  • 65
  • 269
  • 311
3

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.

BoeroBoy
  • 169
2

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.

sch
  • 367
2

Why not use the find command ?

find . -name YOUR_SEARCH_NAME -type d
Scott C Wilson
  • 2,454
  • 5
  • 23
  • 33
2

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, which locate 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.
2

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.

joeytwiddle
  • 1,795
joharr
  • 425
1

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
shrx
  • 474
0

This answer is the inverse of this other. Use

file $(locate -r 'xfce4-keyboard-overlay$') | grep directory$ | awk -F: '{ $(NF--)=""; print }'

Explanation:

  1. locate ... finds all files and directories
  2. file $(...) appends a message at the end of each line with the type of file
  3. grep ... filters lines containing directory$
  4. awk ... removes what file ... appended
0

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.

0

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

tao
  • 1,445