linux系统中whereis的用法详解

10-15

linux/Unix可以使用 whereis 这个命令来查找某个特定的程序和命令的二进制文件()、源代码和man手册的位置,貌似现在还能看到软件的配置文件的位置(路径)。

命令作用:

查找二进制文件位置
查找软件的配置文件的位置
查找源代码文件位置
查看man手册位置

语法

基本语法如下:

whereis command
OR
whereis program
OR
whereis [options] program
OR
whereis -BMS directory -f command
whereis 使用举例:
查找 date 命令的 可执行文件、源代码和man手册的位置,输入:
$ whereis date
输出如下图:
whereis-command-demo.gif
Animated gif 01: whereis command running on my Debian based server
如何只搜索二进制文件的位置?
使用 -b 参数 :
$ whereis -b date
如何只搜索man手册的位置?
使用 -m 参数:
$ whereis -m date
如何只搜索源代码文件的位置?
使用 -s 参数:
$ whereis -s date
问题:whereis -u参数是有问题的,按照man whereis 的说明,-u的就是搜索那些没有二进制文件或者源代码文件或者man手册的文件的。但是实际测试发现,和这毛关系都没有埃
man手册上的一个例子:
A file is said to be unusual if it does not have one entry of each requested type. Thus the following example, asks for those files in the current directory which have no documentation(意思是搜索当前目录下,没有man文档的文件):
$ whereis -m -u *
我们先cd /bin ,然后执行上面的命令,会发现 whereis -m -u * 和 where -m * 结果是一模一样的。-u的功能完全没体现出来。而且与man文档描述的完全不符,因为/bin目录下的文件都是有man文档的,按man文档的意思,结果应该是空的,但是结果却不是空的。
如何限制搜索的路径?
使用下面的参数限制对应的搜索路径:
-B /path/to/dir : 限制在指定的目录搜索二进制文件.
-M /path/to/dir : 限制在指定的目录搜索man手册文件.
-S /path/to/dir : 限制在指定的目录搜索二进制文件.
在使用了-B , -M , -S 任意一个参数时,必须加上 -f 参数, 然后指定要搜索的文件名.
实例如下:只在 /bin 目录下搜索 ls 和gcc的:
$ whereis -B /bin -f ls gcc
结果如下:

1
2
ls: /bin/ls /usr/share/man/man1/ls.1.gz
gcc:
可以看到,gcc在/bin目录下搜索二进制文件是没有结果的,说明gcc的二进制文件不在 /bin目录下。
问题:但是,我发现,虽然/bin目录下没有gcc二进制文件,但是使用上面的命令照样有输出,而不是像我翻译的这篇文章那样。

实例2:这也是man手册上的例子,经测试,这里 -u参数还是和man手册上描述的不符合。
查找所有/usr/bin目录下的,其man文档不在 /usr/man/man1/ ,且其源代码文件不在/usr/src/ 的 文件, 输入:
# cd /usr/bin
# whereis -u -ms -M /usr/man/man1 -S /usr/src -f *
测试:
#cd /bin
#whereis -u -m -M /root -f *
按man手册的意思,这行命令的功能是:查找 所有/bin下,其man文档不在/root的文件。所以应该是有结果输出的,因为/root目录下根本没有任何文件的man手册。可以,惊奇的发现,结果居然是空的。
whereis command options
From the whereis(1) command man page:
Option Meaning
-f Define search scope.
-b Search only binaries.
-B Define binaries lookup path.
-m Search only manual paths.
-M Define man lookup path.
-s Search only sources path.
-S Define sources lookup path.
-u Search from unusual enties.
-V Output version information and exit.无效的,man文档无此参数
-h Display this help and exit. 无效的,man文档无此参数
SEE ALSO
whereis(1) Linux/Unix command man page
Category List of Unix and Linux commands
File Management cat
Network Utilities dig • host • ip
Processes Management bg • chroot • disown • fg • jobs • kill • killall • pwdx • time • pidof • pstree
Searching whereis • which
User Information groups • id • last • lastcomm • logname • users • w • who • whoami • lid • members
关于whereis 的 -u参数的功能,因为不知道whereis的版本,不好查找对应版本的whereis的源代码,我从网上找了个新版本的whereis的c源代码,明显的发现,whereis使用hard-coded paths。
whereis在git上的代码地址:https://github.com/karelzak/util-linux/blob/master/misc-utils/whereis.c#L96

代码如下:

/*-
* Copyright (c) 1980 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* 1999-02-22 Arkadiusz Miśkiewicz <[email protected]>
* - added Native Language Support
* 2011-08-12 Davidlohr Bueso <[email protected]>
* - added $PATH lookup
*
* Copyright (C) 2013 Karel Zak <[email protected]>
* 2013 Sami Kerola <[email protected]>
*/

#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include "xalloc.h"
#include "nls.h"
#include "c.h"
#include "closestream.h"
#include "canonicalize.h"

#include "debug.h"

UL_DEBUG_DEFINE_MASK(whereis);
UL_DEBUG_DEFINE_MASKNAMES(whereis) = UL_DEBUG_EMPTY_MASKNAMES;

#define WHEREIS_DEBUG_INIT (1 << 1)
#define WHEREIS_DEBUG_PATH (1 << 2)
#define WHEREIS_DEBUG_ENV (1 << 3)
#define WHEREIS_DEBUG_ARGV (1 << 4)
#define WHEREIS_DEBUG_SEARCH (1 << 5)
#define WHEREIS_DEBUG_STATIC (1 << 6)
#define WHEREIS_DEBUG_LIST (1 << 7)
#define WHEREIS_DEBUG_ALL 0xFFFF

#define DBG(m, x) __UL_DBG(whereis, WHEREIS_DEBUG_, m, x)
#define ON_DBG(m, x) __UL_DBG_CALL(whereis, WHEREIS_DEBUG_, m, x)

static char uflag = 0;

/* supported types */
enum {
BIN_DIR = (1 << 1),
MAN_DIR = (1 << 2),
SRC_DIR = (1 << 3),

ALL_DIRS = BIN_DIR | MAN_DIR | SRC_DIR
};

/* directories */
struct wh_dirlist {
int type;
dev_t st_dev;
ino_t st_ino;
char *path;

struct wh_dirlist *next;
};

static const char *bindirs[] = {
"/usr/bin",
"/usr/sbin",
"/usr/lib",
"/usr/lib64",
"/bin",
"/sbin",
"/etc",
"/usr/etc",
"/lib",
"/lib64",
"/usr/games",
"/usr/games/bin",
"/usr/games/lib",
"/usr/emacs/etc",
"/usr/lib/emacs/*/etc",
"/usr/TeX/bin",
"/usr/tex/bin",
"/usr/interviews/bin/LINUX",

"/usr/X11R6/bin",
"/usr/X386/bin",
"/usr/bin/X11",
"/usr/X11/bin",
"/usr/X11R5/bin",

"/usr/local/bin",
"/usr/local/sbin",
"/usr/local/etc",
"/usr/local/lib",
"/usr/local/games",
"/usr/local/games/bin",
"/usr/local/emacs/etc",
"/usr/local/TeX/bin",
"/usr/local/tex/bin",
"/usr/local/bin/X11",

"/usr/contrib",
"/usr/hosts",
"/usr/include",

"/usr/g++-include",

"/usr/ucb",
"/usr/old",
"/usr/new",
"/usr/local",
"/usr/libexec",
"/usr/share",

"/opt/*/bin",
NULL
};

static const char *mandirs[] = {
"/usr/man/*",
"/usr/share/man/*",
"/usr/X386/man/*",
"/usr/X11/man/*",
"/usr/TeX/man/*",
"/usr/interviews/man/mann",
"/usr/share/info",
NULL
};

static const char *srcdirs[] = {
"/usr/src/*",
"/usr/src/lib/libc/*",
"/usr/src/lib/libc/net/*",
"/usr/src/ucb/pascal",
"/usr/src/ucb/pascal/utilities",
"/usr/src/undoc",
NULL
};

static void whereis_init_debug(void)
{
__UL_INIT_DEBUG(whereis, WHEREIS_DEBUG_, 0, WHEREIS_DEBUG);
}

static const char *whereis_type_to_name(int type)
{
switch (type) {
case BIN_DIR: return "bin";
case MAN_DIR: return "man";
case SRC_DIR: return "src";
default: return "???";
}
}

static void __attribute__((__noreturn__)) usage(FILE *out)
{
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options] <file>\n"), program_invocation_short_name);

fputs(USAGE_SEPARATOR, out);
fputs(_("Locate the binary, source, and manual-page files for a command.\n"), out);

fputs(USAGE_OPTIONS, out);
fputs(_(" -b search only for binaries\n"), out);
fputs(_(" -B <dirs> define binaries lookup path\n"), out);
fputs(_(" -m search only for manuals and infos\n"), out);
fputs(_(" -M <dirs> define man and info lookup path\n"), out);
fputs(_(" -s search only for sources\n"), out);
fputs(_(" -S <dirs> define sources lookup path\n"), out);
fputs(_(" -f terminate <dirs> argument list\n"), out);
fputs(_(" -u search for unusual entries\n"), out);
fputs(_(" -l output effective lookup paths\n"), out);
fprintf(out, USAGE_MAN_TAIL("whereis(1)"));

exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}

static void dirlist_add_dir(struct wh_dirlist **ls0, int type, const char *dir)
{
struct stat st;
struct wh_dirlist *prev = NULL, *ls = *ls0;

if (access(dir, R_OK) != 0)
return;
if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode))
return;

while (ls) {
if (ls->st_ino == st.st_ino &&
ls->st_dev == st.st_dev &&
ls->type == type) {
DBG(LIST, ul_debugobj(*ls0, " ignore (already in list): %s", dir));
return;
}
prev = ls;
ls = ls->next;
}

ls = xcalloc(1, sizeof(*ls));
ls->st_ino = st.st_ino;
ls->st_dev = st.st_dev;
ls->type = type;
ls->path = canonicalize_path(dir);

if (!*ls0)
*ls0 = ls; /* first in the list */
else {
assert(prev);
prev->next = ls; /* add to the end of the list */
}

DBG(LIST, ul_debugobj(*ls0, " add dir: %s", ls->path));
return;
}

/* special case for '*' in the paths */
static void dirlist_add_subdir(struct wh_dirlist **ls, int type, const char *dir)
{
char buf[PATH_MAX], *d;
DIR *dirp;
struct dirent *dp;

strncpy(buf, dir, PATH_MAX);
buf[PATH_MAX - 1] = '\0';

d = strchr(buf, '*');
if (!d)
return;
*d = 0;

dirp = opendir(buf);
if (!dirp)
return;

DBG(LIST, ul_debugobj(*ls, " scanning subdir: %s", dir));

while ((dp = readdir(dirp)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
snprintf(d, PATH_MAX - (d - buf), "%s", dp->d_name);
/* a dir definition can have a star in middle of path */
strcat(buf, strchr(dir, '*') + 1);
dirlist_add_dir(ls, type, buf);
}
closedir(dirp);
return;
}

static void construct_dirlist_from_env(const char *env,
struct wh_dirlist **ls,
int type)
{
char *key = NULL, *tok = NULL, *pathcp, *path = getenv(env);

if (!path)
return;
pathcp = xstrdup(path);

DBG(ENV, ul_debugobj(*ls, "construct %s dirlist from: %s",
whereis_type_to_name(type), path));

for (tok = strtok_r(pathcp, ":", &key); tok;
tok = strtok_r(NULL, ":", &key))
dirlist_add_dir(ls, type, tok);

free(pathcp);
return;
}

static void construct_dirlist_from_argv(struct wh_dirlist **ls,
int *idx,
int argc,
char *argv[],
int type)
{
int i;

DBG(ARGV, ul_debugobj(*ls, "construct %s dirlist from argv[%d..]",
whereis_type_to_name(type), *idx));

for (i = *idx; i < argc; i++) {
if (*argv[i] == '-') /* end of the list */
break;

DBG(ARGV, ul_debugobj(*ls, " using argv[%d]: %s", *idx, argv[*idx]));
dirlist_add_dir(ls, type, argv[i]);
*idx = i;
}

return;
}

static void construct_dirlist(struct wh_dirlist **ls,
int type,
const char **paths)
{
size_t i;

DBG(STATIC, ul_debugobj(*ls, "construct %s dirlist from static array",
whereis_type_to_name(type)));

for (i = 0; paths[i]; i++) {
if (!strchr(paths[i], '*'))
dirlist_add_dir(ls, type, paths[i]);
else
dirlist_add_subdir(ls, type, paths[i]);
}
return;
}

static void free_dirlist(struct wh_dirlist **ls0, int type)
{
struct wh_dirlist *prev = NULL, *next, *ls = *ls0;

*ls0 = NULL;

DBG(LIST, ul_debugobj(*ls0, "free dirlist"));

while (ls) {
if (ls->type & type) {
next = ls->next;
DBG(LIST, ul_debugobj(*ls0, " free: %s", ls->path));
free(ls->path);
free(ls);
ls = next;
if (prev)
prev->next = ls;
} else {
if (!prev)
*ls0 = ls; /* first unremoved */
prev = ls;
ls = ls->next;
}
}

return;
}

static int filename_equal(const char *cp, const char *dp)
{
int i = strlen(dp);

DBG(SEARCH, ul_debug("compare '%s' and '%s'", cp, dp));

if (dp[0] == 's' && dp[1] == '.' && filename_equal(cp, dp + 2))
return 1;
if (!strcmp(dp + i - 2, ".Z"))
i -= 2;
else if (!strcmp(dp + i - 3, ".gz"))
i -= 3;
else if (!strcmp(dp + i - 3, ".xz"))
i -= 3;
else if (!strcmp(dp + i - 4, ".bz2"))
i -= 4;
while (*cp && *dp && *cp == *dp)
cp++, dp++, i--;
if (*cp == 0 && *dp == 0)
return 1;
while (isdigit(*dp))
dp++;
if (*cp == 0 && *dp++ == '.') {
--i;
while (i > 0 && *dp)
if (--i, *dp++ == '.')
return (*dp++ == 'C' && *dp++ == 0);
return 1;
}
return 0;
}

static void findin(const char *dir, const char *pattern, int *count, char **wait)
{
DIR *dirp;
struct dirent *dp;

dirp = opendir(dir);
if (dirp == NULL)
return;

DBG(SEARCH, ul_debug("find '%s' in '%s'", pattern, dir));

while ((dp = readdir(dirp)) != NULL) {
if (!filename_equal(pattern, dp->d_name))
continue;

if (uflag && *count == 0)
xasprintf(wait, "%s/%s", dir, dp->d_name);

else if (uflag && *count == 1 && *wait) {
printf("%s: %s %s/%s", pattern, *wait, dir, dp->d_name);
free(*wait);
*wait = NULL;
} else
printf(" %s/%s", dir, dp->d_name);
++(*count);
}
closedir(dirp);
return;
}

static void lookup(const char *pattern, struct wh_dirlist *ls, int want)
{
char patbuf[PATH_MAX];
int count = 0;
char *wait = NULL, *p;

/* canonicalize pattern -- remove path suffix etc. */
p = strrchr(pattern, '/');
p = p ? p + 1 : (char *) pattern;
strncpy(patbuf, p, PATH_MAX);
patbuf[PATH_MAX - 1] = '\0';

DBG(SEARCH, ul_debug("lookup dirs for '%s' (%s), want: %s %s %s",
patbuf, pattern,
want & BIN_DIR ? "bin" : "",
want & MAN_DIR ? "min" : "",
want & SRC_DIR ? "src" : ""));
p = strrchr(patbuf, '.');
if (p)
*p = '\0';

if (!uflag)
/* if -u not specified then we always print the pattern */
printf("%s:", patbuf);

for (; ls; ls = ls->next) {
if ((ls->type & want) && ls->path)
findin(ls->path, patbuf, &count, &wait);
}

free(wait);

if (!uflag || (uflag && count > 1))
putchar('\n');
return;
}

static void list_dirlist(struct wh_dirlist *ls)
{
while (ls) {
if (ls->path) {
switch (ls->type) {
case BIN_DIR:
printf("bin: ");
break;
case MAN_DIR:
printf("man: ");
break;
case SRC_DIR:
printf("src: ");
break;
default:
abort();
}
printf("%s\n", ls->path);
}
ls = ls->next;
}
}

int main(int argc, char **argv)
{
struct wh_dirlist *ls = NULL;
int want = ALL_DIRS;
int i, want_resetable = 0;

setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);

if (argc == 1)
usage(stderr);

whereis_init_debug();

construct_dirlist(&ls, BIN_DIR, bindirs);
construct_dirlist_from_env("PATH", &ls, BIN_DIR);

construct_dirlist(&ls, MAN_DIR, mandirs);
construct_dirlist_from_env("MANPATH", &ls, MAN_DIR);

construct_dirlist(&ls, SRC_DIR, srcdirs);

for (i = 1; i < argc; i++) {
const char *arg = argv[i];
int arg_i = i;

DBG(ARGV, ul_debug("argv[%d]: %s", i, arg));

if (*arg != '-') {
lookup(arg, ls, want);
/*
* The lookup mask ("want") is cumulative and it's
* resetable only when it has been already used.
*
* whereis -b -m foo :'foo' mask=BIN|MAN
* whereis -b foo bar :'foo' and 'bar' mask=BIN|MAN
* whereis -b foo -m bar :'foo' mask=BIN; 'bar' mask=MAN
*/
want_resetable = 1;
continue;
}

for (++arg; arg && *arg; arg++) {
DBG(ARGV, ul_debug(" arg: %s", arg));

switch (*arg) {
case 'f':
break;
case 'u':
uflag = 1;
break;
case 'B':
if (*(arg + 1))
usage(stderr);
i++;
free_dirlist(&ls, BIN_DIR);
construct_dirlist_from_argv(
&ls, &i, argc, argv, BIN_DIR);
break;
case 'M':
if (*(arg + 1))
usage(stderr);
i++;
free_dirlist(&ls, MAN_DIR);
construct_dirlist_from_argv(
&ls, &i, argc, argv, MAN_DIR);
break;
case 'S':
if (*(arg + 1))
usage(stderr);
i++;
free_dirlist(&ls, SRC_DIR);
construct_dirlist_from_argv(
&ls, &i, argc, argv, SRC_DIR);
break;
case 'b':
if (want_resetable) {
want = ALL_DIRS;
want_resetable = 0;
}
want = want == ALL_DIRS ? BIN_DIR : want | BIN_DIR;
break;
case 'm':
if (want_resetable) {
want = ALL_DIRS;
want_resetable = 0;
}
want = want == ALL_DIRS ? MAN_DIR : want | MAN_DIR;
break;
case 's':
if (want_resetable) {
want = ALL_DIRS;
want_resetable = 0;
}
want = want == ALL_DIRS ? SRC_DIR : want | SRC_DIR;
break;
case 'l':
list_dirlist(ls);
break;
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
case 'h':
usage(stdout);
default:
usage(stderr);
}

if (arg_i < i) /* moved to the next argv[] item */
break;
}
}

free_dirlist(&ls, ALL_DIRS);
return EXIT_SUCCESS;
}

靠,就连代码注释都有错误,懒得折腾了,哥还有事要做。反正 -u 参数实际很少使用的,有兴趣的可以看下这代码。