15

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?

1

11 Answers 11

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.

8
  • 1
    To search for all directories, locate -r '/[^\.]*$'. Feb 17, 2018 at 23:26
  • 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 M
    Apr 3, 2018 at 21:33
  • 1
    Doesn't work if dirname is in the middle of the dir name.
    – CodyChan
    Feb 9, 2021 at 18:56
  • @CodyChan: I was assuming "dirname" is the entire directory name. Can you show an example where this doesn't work?
    – Frank M
    Feb 10, 2021 at 22:38
  • string is part of the directory name such as I need to search directory whose name contains def 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.
    – CodyChan
    Feb 12, 2021 at 17:54
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 {} +
17
  • 2
    @sch Wow, that sucks Apr 2, 2015 at 8:39
  • 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 using eval with arbitrary data (hence the downvote).
    – sch
    Apr 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 with sudo ;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
    – sch
    Feb 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 of xargs overall exit status. Using proper if constructs and do a exit 255 if printf fails so xargs aborts as well may be preferable.
    – sch
    Feb 6, 2023 at 17:29
2

Why not use the find command ?

find . -name YOUR_SEARCH_NAME -type d
1
  • 9
    locate is faster and I don't need it to be up to date all the time for my purpose.
    – shrx
    Apr 3, 2012 at 20:30
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
  • Yeah root access for a task like this should really be avoided.
    – shrx
    Apr 3, 2012 at 20:32
  • Does this list matching files as well as matching directories? Dec 22, 2020 at 6:07
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.

2
  • 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'
    – user128063
    Nov 9, 2018 at 20:30
  • Use locate -0 and xargs -0 flags if you get the xargs unterminated quote error.
    – usr122212
    Dec 25, 2019 at 1:53
2

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.

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.

2
  • 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.
    – sch
    Feb 8, 2023 at 8:10
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
2
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

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .