如何编程实现Linux中的ls命令?

版权声明:本文为博主原创文章,未经博主允许不得转载。有事联系:[email protected] https://blog.csdn.net/qq_17550379/article/details/89406272

0x01 ls命令是什么?

我使用的系统环境是Ubuntu 18.04,通过ls命令我们可以知道当前目录下有哪些文件:

ls命令还有常用的参数:

  • -l:列出文件的详细信息
  • -a:列出包含.开头的文件
  • -s:文件大小以块为单位打印
  • -t:按时间排序输出
  • -F:显示文件类型
  • -r:逆序输出
  • -R:递归输出
  • -q:不排序输出

0x02 使用函数介绍

因为有读写文件操作,我们这里的操作主要有

0x0201 目录打开

所需要的头文件

#include <sys/types.h>
#include <dirent.h>

函数的格式

DIR *opendir(const char *name);

name指向我们需要打开的目录,返回的是一个DIR结构体指针(可以理解为“目录描述符”)。

0x0202 目录的读操作

所需要的头文件

#include <dirent.h>

函数的格式

struct dirent *readdir(DIR *dirp);

dirp指向我们的目录描述符,返回的是一个dirent结构体。

struct dirent {
    ino_t          d_ino;       /* Inode number */
    off_t          d_off;       /* Not an offset; see below */
    unsigned short d_reclen;    /* Length of this record */
    unsigned char  d_type;      /* Type of file; not supported
                                              by all filesystem types */
    char           d_name[256]; /* Null-terminated filename */
};

我们可以通过d_name获取目录中的文件信息。

0x0203 目录的关闭操作

所需的头文件

#include <sys/types.h>
#include <dirent.h>

函数原型

int closedir(DIR *dirp);

dirp指向我们的目录描述符,关闭的过程中出现错误返回-1

0x0204 获取文件信息

所需的头文件

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

函数原型

int stat(const char *pathname, struct stat *statbuf);

pathname指向我们需要获取的文件,stat是一个结构体包含文件信息。

struct stat {
    dev_t     st_dev;         /* ID of device containing file */
    ino_t     st_ino;         /* Inode number */
    mode_t    st_mode;        /* File type and mode */
    nlink_t   st_nlink;       /* Number of hard links */
    uid_t     st_uid;         /* User ID of owner */
    gid_t     st_gid;         /* Group ID of owner */
    dev_t     st_rdev;        /* Device ID (if special file) */
    off_t     st_size;        /* Total size, in bytes */
    blksize_t st_blksize;     /* Block size for filesystem I/O */
    blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

    /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

    struct timespec st_atim;  /* Time of last access */
    struct timespec st_mtim;  /* Time of last modification */
    struct timespec st_ctim;  /* Time of last status change */

#define st_atime st_atim.tv_sec      /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

获取信息出错的话返回-1

0x0205 获取用户的passwd信息

所需的头文件

#include <sys/types.h>
#include <pwd.h>

函数原型

    struct passwd *getpwuid(uid_t uid);

uid就是我们的用户ID,返回的是一个passwd结构体指针

struct passwd
{
  char *pw_name;                /* Username.  */
  char *pw_passwd;              /* Password.  */
  __uid_t pw_uid;               /* User ID.  */
  __gid_t pw_gid;               /* Group ID.  */
  char *pw_gecos;               /* Real name.  */
  char *pw_dir;                 /* Home directory.  */
  char *pw_shell;               /* Shell program.  */
};

0x0206 获取组ID信息

所需的头文件

#include <sys/types.h>
#include <grp.h>

函数原型

struct group *getgrgid(gid_t gid);

gid就是我们的组ID,返回的是一个group结构体指针

struct group
{
    char *gr_name;              /* Group name.  */
    char *gr_passwd;            /* Password.    */
    __gid_t gr_gid;             /* Group ID.    */
    char **gr_mem;              /* Member list. */
};

0x03 编写一个ls

0x0301 第一版

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

void do_ls(char []);

void do_ls(char dirname[])
{
    DIR* dir_ptr;
    struct dirent* direntp;
    if ((dir_ptr = opendir(dirname)) == NULL)
    {
        fprintf(stderr, "ls1: cannot open %s\n", dirname);
    }
    else
    {
        while ((direntp = readdir(dir_ptr)) != NULL)
        {
            printf("%s\n", direntp->d_name);
        }
        closedir(dir_ptr);
    }
}

int main(int argc, char *argv[])
{
    if (argc == 1) do_ls(".");
    else
    {
        while (--argc)
        {
            printf("%s:\n", *(++argv));
            do_ls(*argv);
        }
    }
}

我们查看我们的版本和实际版本的区别

功能实现的还不错,输出的格式需要调整一下。我们的输出需要排序一下。ls并没有输出.开头的文件,需要在-a中。我们还要添加一个-l功能。

0x0302 第二版

我们首先看看ls -l会打印哪些信息

$ ls -l
total 24
-rwxr-xr-x 1 lly lly 8568 4月 19 09:54 app
-rw-r–r-- 1 lly lly 589 4月 19 09:52 ls.c
-rw-r–r-- 1 lly lly 2256 4月 19 09:54 ls.o
-rw-r–r-- 1 lly lly 249 4月 19 09:54 Makefile

每行包含7个字段,分别是模式链接数文件所有者文件所有组文件大小最后修改时间文件名,这些信息可以从stat获取。

我们先编写一个函数show_stat_info,看看能获取什么信息。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

void show_stat_info(char *fname, struct stat* buf);

void show_stat_info(char *fname, struct stat* buf)
{
    printf("mode:%o\n", buf->st_mode);
    printf("links:%d\n", buf->st_nlink);
    printf("user:%d\n", buf->st_uid);
    printf("group:%d\n", buf->st_gid);
    printf("size:%d\n", buf->st_size);
    printf("modtime:%d\n", buf->st_mtime);
    printf("name:%s\n", fname);
}
int main(int argc, char *argv[])
{
    struct stat info;
    if (argc > 1)
    {
        if (stat(argv[1], &info) != -1)
        {
            show_stat_info(argv[1], &info);
            return 0;
        }
        else perror(argv[1]);
    }
    return 1;
}

查看我们现在获取的信息和ls -l获取的有什么不同

我们需要将mode转化为字符的形式,怎么转化?在/usr/include/bits/stat.h中找到下面信息

#define __S_IFDIR       0040000 /* Directory.  */
#define __S_IFCHR       0020000 /* Character device.  */
#define __S_IFBLK       0060000 /* Block device.  */
#define __S_IFREG       0100000 /* Regular file.  */
#define __S_IFIFO       0010000 /* FIFO.  */
#define __S_IFLNK       0120000 /* Symbolic link.  */
#define __S_IFSOCK      0140000 /* Socket.  */

/usr/include/sys/stat.h找到下面的一些宏函数

#define S_ISDIR(mode)    __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode)    __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode)    __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode)    __S_ISTYPE((mode), __S_IFREG)
#ifdef __S_IFIFO
# define S_ISFIFO(mode)  __S_ISTYPE((mode), __S_IFIFO)
#endif
#ifdef __S_IFLNK
# define S_ISLNK(mode)   __S_ISTYPE((mode), __S_IFLNK)
#endif

所以我们现在可以写一个mode_to_letters函数

void mode_to_letters(int mode, char str[])
{
    strcpy(str, "----------");
    if (S_ISDIR(mode)) str[0] = 'd';
    if (S_ISCHR(mode)) str[0] = 'c';
    if (S_ISBLK(mode)) str[0] = 'b';

    if (mode & S_IRUSR) str[1] = 'r';
    if (mode & S_IWUSR) str[2] = 'w';
    if (mode & S_IXUSR) str[3] = 'x';

    if (mode & S_IRGRP) str[4] = 'r';
    if (mode & S_IWGRP) str[5] = 'w';
    if (mode & S_IXGRP) str[6] = 'x';

    if (mode & S_IROTH) str[7] = 'r';
    if (mode & S_IWOTH) str[8] = 'w';
    if (mode & S_IXOTH) str[9] = 'x';
}

ok,现在我们的mode问题解决了。接着我们看usergroupbash输出的不同。

我们知道用户的信息文件是放在了/etc/passwd,我们可以通过getpwuid获取该文件的信息。对于用户的组信息,我们可以通过getgrgid获取。我们可以很快写出两个获取信息的函数

char *uid_to_name(uid_t uid)
{
    struct passwd* pw_ptr;
    static char numstr[10];

    if ((pw_ptr = getpwuid(uid)) == NULL)
    {
        sprintf(numstr, "%d", uid);
        return numstr;
    }else return pw_ptr->pw_name;
}

char *gid_to_name(gid_t gid)
{
    struct group* grp_ptr;
    static char numstr[10];

    if ((grp_ptr = getgrgid(gid)) == NULL)
    {
        sprintf(numstr, "%d", gid);
        return numstr;
    }
    else return grp_ptr->gr_name;
}

好的,至此基本功能实现了。我将代码放到了GitHub Linux上。

但是我们这个代码有一个问题就是没有显示记录总数,并且没有按文件名排序,也不支持选项-a。现有的函数也没法显示特殊文件格式suidsgidsticky。怎么添加-R功能?怎么添加-r功能?怎么添加-q功能?

0x0303 第三版

先处理一个简单的问题,添加特殊文件格式suidsgidsticky

void mode_to_letters(int mode, char str[])
{
    strcpy(str, "----------");
    if (S_ISDIR(mode)) str[0] = 'd';
    if (S_ISCHR(mode)) str[0] = 'c';
    if (S_ISBLK(mode)) str[0] = 'b';
    if (S_ISREG(mode)) str[0] = '-';
    if (S_IFIFO(mode)) str[0] = 'p';
    if (S_IFLNK(mode)) str[0] = 'l';
    if (S_IFSOCK(mode)) str[0] = 's';

    if (mode & S_IRUSR) str[1] = 'r';
    if (mode & S_IWUSR) str[2] = 'w';
    if (mode & S_IXUSR) str[3] = 'x';
    if (mode & S_ISUID) str[3] = 's';

    if (mode & S_IRGRP) str[4] = 'r';
    if (mode & S_IWGRP) str[5] = 'w';
    if (mode & S_IXGRP) str[6] = 'x';
    if (mode & S_ISGID) str[6] = 's';

    if (mode & S_IROTH) str[7] = 'r';
    if (mode & S_IWOTH) str[8] = 'w';
    if (mode & S_IXOTH) str[9] = 'x';
    if (mode & S_ISVTX) str[9] = 't';
}

我将该问题的其他语言版本添加到了我的GitHub Linux

如有问题,希望大家指出!!!

猜你喜欢

转载自blog.csdn.net/qq_17550379/article/details/89406272