65.9K
CodeProject 正在变化。 阅读更多。
Home

在MacOS Unix上使用Locate数据库

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2020年10月14日

CPOL

8分钟阅读

viewsIcon

10981

在MacOS Unix上使用文件名数据库

引言

和所有Unix系统一样,MacOS也支持文件名数据库,这些数据库是Unix findutils的一部分。文件名数据库对不经常使用Unix的用户来说相对陌生,但它们提供了一些有用的功能,值得探索。

背景

我自己在网上搜索如何整理Mac上的文件时,偶然发现了文件名数据库功能。虽然文件名数据库不能直接整理你的文件,但它们在按名称搜索文件时会变得非常有用。

Using the Code

通常,你会使用Unix的find命令按名称搜索文件。例如,如果你想在当前目录及其所有子文件夹中查找所有扩展名为jpg的文件,你会在Unix提示符下输入:

$ find . -name *.jpg -print

这工作得很好,但是find每次调用时都会对它要搜索的文件系统进行一次完整的扫描。如果需要搜索的目录不多,这仍然是高效的。但是,如果你需要在一个更大的目录结构中反复搜索文件,使用文件名数据库会更有效率。

文件名数据库是一个包含文件名列表的文件,包括文件所在的完整路径。使用文件名数据库搜索文件时,你不会扫描目录结构,而只是在文件名数据库中查找文件和路径,这对于大型目录结构来说当然要快得多。

因此,要在你的主目录及其所有子文件夹中搜索所有扩展名为jpg的文件,你会在Unix提示符下输入:

$ locate $HOME/*.jpg

然而,缺点是,为了能够在搜索中使用文件名数据库,你需要首先构建一个,然后定期更新它。

构建文件名数据库

在Unix提示符下使用locate命令之前,你需要先构建一个文件名数据库,这通过运行/usr/libexec/locate.updatedb命令来完成。

可以直接运行该命令,但建议先查看用于构建文件名数据库的设置。

构建文件名数据库的设置

/usr/libexec/locate.updatedb命令从以下变量获取构建文件名数据库的设置:

  • TMPDIR:这是用于临时文件的目录。
  • FCODES:此变量保存文件名数据库的名称和路径。
  • SEARCHPATHS:此变量保存要搜索的路径列表。
  • PRUNEPATHS:此变量保存SEARCHPATHS路径内要排除的路径列表。

这些变量的值设置如下:

  • /usr/libexec/locate.updatedb命令首先检查环境变量LOCATE_CONFIG。如果它设置为文件名,则变量设置将从该文件中获取。
  • 如果环境变量LOCATE_CONFIG未设置,则变量设置将从/etc/locate.rc文件中获取。
  • 如果/etc/locate.rc文件不存在或不包含任何设置,将使用/usr/libexec/locate.updatedb中硬编码的默认值。

通常,LOCATE_CONFIG环境变量未设置,/etc/locate.rc文件也没有任何变量设置,因此使用的默认值是:

  • TMPDIR="/tmp"
  • FCODES="/var/db/locate.database"
  • SEARCHPATHS="/"
  • PRUNEPATHS="/tmp /var/tmp"

因此,在这些设置下,/usr/libexec/locate.updatedb命令将搜索从根目录 (/) 开始的完整目录结构,排除目录/tmp /var/tmp

乍一看,这些设置似乎是一个很好的起点,但是它有一个问题,因为只有运行命令的用户实际有权访问的文件才会被添加到文件名数据库中。

另一个问题是,由于locate.updatedb的内部工作原理,即使以超级用户身份使用sudo运行它,也无法获得完整的文件列表。

locate.updatedb的内部工作原理

命令locate.updatedb实际上是一个Unix shell脚本,它主要执行以下操作:

  1. 如果它由超级用户调用(使用sudo),它会以用户nobody递归地调用自身。否则(这在作为用户nobody的递归调用中也是如此),它直接开始下一个步骤。
  2. 它调用另一个Unix脚本/usr/libexec/locate.mklocatedb,该脚本使用find命令搜索由SEARCHPATHS变量指定的目录树(或多个树)中所有文件(省略由PRUNEPATHS变量指定的子树),并将它们及其完整路径写入临时文件名数据库。
  3. 它将临时文件名数据库的内容复制到由FCODES变量指定的名称和位置。

这意味着,如果你以超级用户身份使用sudo运行它,你最终会得到一个文件名数据库,其中只包含用户nobody有权访问的文件名。

这种行为的原因是:由于Unix是一个多用户系统,并且文件名数据库可供每个用户访问,用户可能会查询彼此的目录结构和其中的文件名,而这些是他们通常无法访问的。

因此,更明智的方法是为每个用户拥有单独的文件名数据库,其中包含其各自主目录的目录结构和文件,并为所有其他目录(不包括用户主目录)拥有一个单一的中央数据库。

创建中央文件名数据库

要创建如上一节所述的排除用户主目录的中央文件名数据库,请按如下方式编辑/etc/locate.rc文件:

$ sudo -e /etc/locate.rc

#
# /etc/locate.rc -  command script for updatedb(8)
#
# $FreeBSD: src/usr.bin/locate/locate/locate.rc,v 1.9 2005/08/22 08:22:48 cperciva Exp $

#
# All commented values are the defaults
#
# temp directory
TMPDIR="/tmp"

# the actual database
#FCODES="/var/db/locate.database"

# directories to be put in the database
SEARCHPATHS="/"

# directories unwanted in output
PRUNEPATHS="/tmp /var/tmp /Users /Volumes"

# filesystems allowed. Beware: a non-listed filesystem will be pruned
# and if the SEARCHPATHS starts in such a filesystem locate will build
# an empty database.
#
# be careful if you add 'nfs'
FILESYSTEMS="hfs ufs apfs"

这将搜索从根目录 (/) 开始的目录结构,省略/tmp /var/tmp /Users/Volumes。您可能已经注意到,FCODES变量没有被注释掉。有关其原因,请参阅下面的兴趣点部分。

一旦您对/etc/locate.rc进行了更改,您就可以以超级用户身份运行/usr/libexec/locate.updatedb来创建文件名数据库:

$ sudo /usr/libexec/locate.updatedb

如果没有打印任何输出,则命令成功完成,您将能够使用此文件名数据库定位文件。您可以通过尝试以下示例来检查这一点(重要的是在*之前以/开头):

$ locate /*.txt

$ locate /*.jpg

这些应该返回或多或少的冗长输出。

要检查用户目录是否未被扫描,请运行以下命令:

$ locate /Users

这不应该返回用户目录中的任何文件。

为每个用户创建单独的文件名数据库

要为用户主目录中的目录和文件创建单独的文件名数据库,首先将/etc/locate.rc文件复制到/etc/locate.users.rc,然后按如下方式编辑它:

$ sudo cp /etc/locate.rc /etc/locate.users.rc

$ sudo -e /etc/locate.users.rc

#
# Configuration for user home directory search
#
# temp directory
TMPDIR="/tmp"

# the actual database
FCODES="$HOME/locate.user.database"

# directories to be put in the database
SEARCHPATHS="$HOME"

# directories unwanted in output
# PRUNEPATHS="/tmp /var/tmp /Users /Volumes"

# filesystems allowed. Beware: a non-listed filesystem will be pruned
# and if the SEARCHPATHS starts in such a filesystem locate will build
# an empty database.
#
# be careful if you add 'nfs'
FILESYSTEMS="hfs ufs apfs"

一旦您对/etc/locate.users.rc进行了更改,您就可以按如下方式运行/usr/libexec/locate.updatedb开始创建:

$ export LOCATE_CONFIG="/etc/locate.users.rc";/usr/libexec/locate.updatedb

如果没有打印任何输出,则命令成功完成,您将能够使用此文件名数据库定位文件。您可以通过尝试以下示例来检查这一点(重要的是在*之前以/开头):

$ locate -d $HOME/locate.user.database /*.txt

$ locate -d $HOME/locate.user.database /*.jpg

这些应该返回或多或少包含您主目录中文件的冗长输出。-d选项告诉locate使用用户的个人文件名数据库。

更新文件名目录

随着文件不断被添加、重命名或删除,以及目录结构也在不断变化,您需要定期更新文件名目录。要更新文件名目录,您需要遵循与上述构建它们相同的步骤,无论是手动操作还是通过/System/Library/LaunchDaemons/com.apple.locate.plist作业。

手动更新中央文件名数据库时,请首先检查LOCATE_CONFIG的值是否未指向用户文件名数据库的配置。

关注点

/usr/libexec/locate.updatedb脚本的实现方式是,当以超级用户身份调用时,它会在步骤1中创建一个临时文件名数据库(参见上面的locate.updatedb的内部工作原理部分),然后将其作为变量FCODES的值传递给作为用户nobody的递归调用,并在步骤3中将另一个临时文件名数据库的内容复制到其中。然而,在步骤2中,如果设置了配置文件的FCODES值(/etc/locate.rc或由LOCATE_CONFIG变量指定),它将被加载并覆盖FCODES的值。

非常令人困惑,但总而言之,如果您在locate.updatedb的配置文件中将FCODES设置为一个值(即使是默认值),并以超级用户身份调用脚本,您将收到以下错误消息,并且脚本将被中止:

/usr/libexec/locate.updatedb: line 97: /var/db/locate.database: Permission denied

此外,当以超级用户身份调用时,脚本将/var/db/locate.database硬编码为最终的文件名数据库,因此即使没有“权限被拒绝”错误,来自/etc/locate.rc的值也不会被用作文件名数据库的最终名称。

我已附上一个脚本,以说明如何解决这些问题。

历史

  • 2020年10月15日:初始版本
© . All rights reserved.