locateコマンドを用いてファイルを高速検索


locateコマンドはファイルシステムのデータベースを検索することで高速で目的のファイルの絶対パスを探し出すことができるコマンドです。

$ man -a locate

#################################################################

locate(1)                                                                                          locate(1)

NAME
       locate - find files by name

SYNOPSIS
       locate [OPTION]... PATTERN...

DESCRIPTION
       locate  reads  one  or more databases prepared by updatedb(8) and writes file names matching at least
       one of the PATTERNs to standard output, one per line.

       If --regex is not specified, PATTERNs can contain globbing characters.  If any  PATTERN  contains  no
       globbing characters, locate behaves as if the pattern were *PATTERN*.

       By  default,  locate  does  not  check whether files found in database still exist.  locate can never
       report files created after the most recent update of the relevant database.

#################################################################



#以前どこかに置いたcoreutils-8.4.tar.gzを探す
$ locate coreutils-8.4.tar.gz
/home/kappa/Documents/1203/0301/coreutils-8.5/lib/coreutils-8.4.tar.gz

#以前どこかに置いたcoreutils-8.4.taとしても*を補って探し出してくれる
$ locate coreutils-8.4.ta
/home/kappa/Documents/1203/0301/coreutils-8.5/lib/coreutils-8.4.tar.gz


#こちらがワイルドカードを補うのは余計なお節介のようである。

$ locate coreutils-8.4.ta*

$



locateコマンドは高速で便利である。

プログラムからUNIXコマンドを呼び出す

system関数を使うとcプログラムの中からシェルコマンドを呼び出せることを知ったのでメモしておきます。

$ man -a system

#################################################################

SYSTEM(3)                                 Linux Programmer's Manual                                SYSTEM(3)

NAME
       system - execute a shell command

SYNOPSIS
       #include <stdlib.h>

       int system(const char *command);

DESCRIPTION
       system() executes a command specified in command by calling /bin/sh -c command, and returns after the
       command has been completed.  During execution of the command, SIGCHLD will be blocked, and SIGINT and
       SIGQUIT will be ignored.


#################################################################


「/bin/sh -c command」について調べてみる

$ man -a bash
#################################################################


BASH(1)                                                                                              BASH(1)

NAME
       bash - GNU Bourne-Again SHell

SYNOPSIS
       bash [options] [file]

・・・・・・・・・・・

OPTIONS
       In  addition  to  the single-character shell options documented in the description of the set builtin command, bash interprets the following options when it is invoked:

       -c string If the -c option is present, then commands are read from string.  If  there  are  arguments after the string, they are assigned to the positional parameters, starting with $0.




#################################################################


以上の情報をまとめると、system関数は/bin/sh -cを与えられた引数をつけて実行することができます。

ディレクトリのファイルを閲覧するlsコマンドを実際に呼び出してみます。

/* myls.c */
#include<stdlib.h>      /* system */

int main(void)
{
        system("ls");
        return 0;
}
/* ここまで*/

$ ./myls
errno mybash    myls    myrdir   myrmdir2 myrmdir3    rmdir.c  test.c  test1.c
errno.c  mybash.c  myls.c  myrmdir  myrmdir2.c myrmdir3.c  test     test1









rmdirを作る

空のディレクトリを削除するコマンド「rmdir」をシステムコールを用いて作ってみたいと思います。

まずは、オンラインマニュアルを見ます

$ man -a rmdir

##################################################################

RMDIR(1)                                                                                             RMDIR(1)

名前
       rmdir - 空のディレクトリを削除する

書式
       rmdir [options] directory...

       POSIX オプション: [-p]

       GNU オプション (簡略形式): [-pv] [--ignore-fail-on-non-empty] [--help] [--version] [--]

説明
       rmdir コマンドは空のディレクトリを削除する。

       引き数に指定した directory が存在する空のディレクトリでない場合にはエラーになる。

#################################################################

#################################################################

RMDIR(2)                                  Linux Programmer's Manual                                  RMDIR(2)

NAME
       rmdir - delete a directory

SYNOPSIS
       #include <unistd.h>

       int rmdir(const char *pathname);

DESCRIPTION
       rmdir() deletes a directory, which must be empty.

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

#################################################################


マニュアルには第一章と二章の両方にrmdirの項目があることがわかった。
今回は第二章の内容を活用します。


今、カレントディレクトリにtestという空のディレクトリを作ります。

$ mkdir test

このディレクトリを削除するためのコマンドを作成します。


/* myrdir.c */
#include<unistd.h>

int main(void)
{
         rmdir("./test");
         return 0;
}

これをコンパイルして実行すれば、testディレクトリは削除されます。

次に、このプログラムに、ディレクトリが存在しない時のエラー処理を追加します。

エラーメッセージについては、errnoに格納されるエラー番号をperrorに与えることで表示します。

$ man errno

################################################################

ERRNO(3)                                  Linux Programmer's Manual                                 ERRNO(3)

NAME
       errno - number of last error

SYNOPSIS
       #include <errno.h>

・・・・・・・・・・・

   ENOENT          No such file or directory (POSIX.1)


################################################################


$ man -a perror

################################################################

PERROR(3)                                 Linux Programmer's Manual                                PERROR(3)

NAME
       perror - print a system error message

SYNOPSIS
       #include <stdio.h>

       void perror(const char *s);

       #include <errno.h>

       const char *sys_errlist[];
       int sys_nerr;
       int errno;
################################################################



/* errno.c */
#include<stdio.h>       /* perrorのプロトタイプ宣言 */
#include<errno.h>       /* errnorの変数宣言 */

int main(void)
{
        errno = ENOENT;
        perror("TEST");
        return 0;
}
/*ここまで*/

$ gcc -o errno errno.c

$ ./errno
TEST: No such file or directory

このプログラムを上記のmyrmdirに組み込みます。

/* myrmdir2.c */

#include<unistd.h>      /* rmdir */
#include<stdlib.h>      /* exit */
#include<stdio.h>       /* perror */

int main(void)
{
        if(rmdir("test") == -1){
                //rmdir()が-1を返した時は、errnoからエラー番号を取得して、
                //エラーメッセーを表示して、プログラムを終了する。
                perror("myrmdir2");
                exit(1);
        }
}


$ mkdir test

$ ./myrmdir2
$ ./myrmdir2
myrmdir2: No such file or directory





エラーメッセージの表示

C言語でUNIXコマンドのエラーメッセージと同等のものを表示することを考えます。

システムコールやライブラリ関数が成功したかどうかを調べて、失敗した場合に適切な処理をすることをエラー処理と言います。これらは失敗時に外部変数(errno.hにマクロにより定義されている)にエラー番号をセットします。

$ man errno


NAME
       errno - number of last error

SYNOPSIS
       #include <errno.h>

DESCRIPTION
       The <errno.h> header file defines the integer variable errno, which is set by system calls
       and some library functions in the event of an error to  indicate  what  went  wrong.


以下のプログラムでは、エラー番号を読み取りそのエラー番号が意味するエラーメッセージを標準エラー出力に出力します。



/* errno.c */
#include<stdio.h>
#include<errno.h>
int main(void)
{
        errno = ENOENT;
        perror("test");
}



$ ./errno
test: No such file or directory




ヘッダファイルの役割

C言語プログラムの冒頭でincludeすることの多いstdio.hについて書きます。
stdio.hにはprintf関数のプロトタイプ宣言が書いてあるだけで、関数の本体は/usr/lib/libc.aの中にあります。
/usr/lib/libc.aの中に葉C標準ライブラリ関数のプログラムがバイナリコード形式で格納されています。
libc.aは-lmのオプションをつけなくてもコンパイルのときに自動的にリンクされます。
したがって、printfのプロトタイプ宣言を行ってあげれば、stdio.hファイルをインクルードする必要はないのです。
printfのプロトタイプ宣言の書き方を調べるには、UNIXオンラインマニュアルのSYNOPSISを調べます。

$ man 3 printf


SYNOPSIS
       #include <stdio.h>

       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
       int sprintf(char *str, const char *format, ...);
       int snprintf(char *str, size_t size, const char *format, ...);

よって以下のようにすればHelloworldをヘッダファイルのインクロードなしでコンパイルすることが加納です。

/* hello.c */

int printf(const char *, ...);
int main(void)
{
        printf("Hello, world!!\n");

        return 0;
}

$ gcc -o hello hello.c
$ ./hello
Hello, world