要求:利用C语言实现Shell指令ls -l

这里我们只在ubuntu的终端里面输入ls -l指令,看看得到结果。

 

在这里我们可以看到终端将当前文件夹内的文件按以下格式显示:

文件类型文件权限目录(链接个数)所属用户所属用户组文件大小修改时间文件名称
drwxrwxr-x3linuxlinux40962月  3  18:29A

在linux中Shell指令ls -l指令的作用是:以列表的形式显示指定文件内文件的详细信息,无参数则默认显示当前目录。

所以我们在使用C语言实现ls -l指令的时候,需要按照下面步骤:

第一步:利用opendir()函数去打开某个目录。

第二步:利用readdir()函数去读取该目录下的所有文件信息。 

第三步:利用stat()函数,读取文件信息,包括其文件类型,文件权限,文件链接数,文件大小,文件修改时间(UTC)等等

第四步:利用getpwuid()函数,获取文件所属用户名,利用getgrgid()函数,获取文件所属用户组名

第五步:利用locatime()函数,将文件修改时间由UTC转换为所属地区时间

这里我们需要注意的是,readdir()函数每次只会读取到一个文件名,所以每次进行进行第二步读取文件名信息,执行完后面的三四五步后,程序应该重新跳到第二步继续读取下一个文件名,直到readdir()函数读取不到文件名为止。

 

分步实现:

 这里面涉及的函数较多,及结构体变量较多,建议在终端打开多个窗口用于查询函数和结构体变量的介绍。

第一步:打开目录,需要使用opendir()函数,函数介绍如下

函数原型函数作用
DIR *opendir(const char *name);打开一个目录流,并返回指向目录流的指针,打开失败返回NULL
name:目录名(路径)

第二步:读取该目录下的文件信息,需要使用readdir()函数,函数介绍如下

函数原型函数作用
struct dirent * readdir(DIR * dir);

返回一个指向当前目录流的指针结构体,当到达目录流或的末尾时或者发生错误,它返回NULL

dir:指向目录流的指针

指针结构体为:

struct dirent{

       ino_t             d_ino;         // inode number 
       off_t             d_off;         // not an offset; see NOTES 
       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];   // filename 此项即为我们需要的文件名 

}

这里我们需要注意的是,每次调用该函数会按序得到一个文件的信息,同时内部指针会自动移位,再次调用的时候,会得到下一个文件的信息。所以在程序中不断调用该函数就可以得到查看目录下的所以文件信息。

并且再之前分析上,我们第二步是需要不断循环的,所以在程序中可以这样做:

//p_dir为指向目录的指针
//d_dir为指向目录流的结构体指针

while(d_dir = readdir(p_dir))    //不断调用能按序得到目录下的所有文件信息,当返回值为NULL时,循环打断
{
    printf("filename is %s \n", d_dir->d_name); //打印文件名
    //第三步
    //第四步
    //第五步
}

第三、第四、第五步没有顺序之分,这里我们按照--> ls -l命令显示格式来按序实现:

首先利用stat()函数,得到文件的具体信息:

函数原型函数作用
int stat(const char *path, struct stat *buf);通过路径path获取文件信息,并保存在buf所指的结构体stat中
path:路径         buf:文件信息保存区

struct stat结构体详细信息如下:

struct stat {
    dev_t     st_dev;     // ID of device containing file 
    ino_t     st_ino;     // inode number 
    mode_t    st_mode;    // protection                      文件类型及权限保存在这 
    nlink_t   st_nlink;   // number of hard links            文件连接数
    uid_t     st_uid;     // user ID of owner                文件所属用户ID    
    gid_t     st_gid;     // group ID of owner               文件所属组ID
    dev_t     st_rdev;    // device ID (if special file) 
    off_t     st_size;    // total size, in bytes            文件大小 
    blksize_t st_blksize; // blocksize for filesystem I/O 
    blkcnt_t  st_blocks;  // number of 512B blocks allocated 
    time_t    st_atime;   // time of last access 
    time_t    st_mtime;   // time of last modification       文件最新修改时间 
    time_t    st_ctime;   // time of last status change 
};

这里我们先利用st_mode获取文件的类型:

  在man帮助手册中,我们可以查到关于st_mode的介绍,

The following flags are defined for the st_mode field:

nameoctaldescription
S_IFMT0170000bit mask for the file type bit fields
S_IFSOCK0140000socket
S_IFLNK0120000symbolic link
S_IFREG0100000regular file
S_IFBLK0060000block device
S_IFDIR0040000directory
S_IFCHR0020000character device
S_IFIFO0010000FIFO

     所以关于文件类型就可以这样区分:

switch (st.st_mode & S_IFMT) 
{
    case S_IFBLK:  printf("b");        break;//块设备类型
            
    case S_IFCHR:  printf("c");        break;//字符类型
            
    case S_IFDIR:  printf("d");        break;//目录类型       
            
    case S_IFIFO:  printf("p");        break;//管道类型       
            
    case S_IFLNK:  printf("l");        break;//链接类型         
            
    case S_IFREG:  printf("-");        break;//文件类型    
            
    case S_IFSOCK: printf("s");        break;//套接字类型           
            
    default:       printf("?");       break;//无法识别类型        
            
}

下来我们来实现判断文件的权限,文件的权限信息同样保存在st_mode当中:

nameoctaldescription
S_IRUSR00400owner has read permission
S_IWUSR00200owner has write permission
S_IXUSR00100owner has execute permission
S_IRGRP00040group has read permission
S_IWGRP00020group has write permission
S_IXGRP00010group has execute permission
S_IROTH00004others have read permission
S_IWOTH00002others have write permission
S_IXOTH00001others have execute permission

        文件权限显示格式:【文件或文件夹】【owner权限】【group权限】【others权限】
         在这里我们使用一种洁简的方式显示,文件权限的显示:

for (i = 8; i >= 0; i--)
{
    if (st.st_mode & (1 << i))
    {
        switch( i % 3)
        {
            case 2:    printf("r");    break;    //文件该处具有读权限
            
            case 1:    printf("w");    break;    //文件该处具有写权限
        
            case 0:    printf("x");    break;    //文件该处具有执行权限
        }
    }
    else
    {
        printf("-");                             //文件该处无任何权限
    }
}

下面获取文件的链接数,该项就是struct stat中的st_nlink变量。我们直接读取其值即可。

printf("file links is %d \n", st.st_nlink);

下面获取文件所属用户和获取文件所属用户组,由于在struct stat变量中的st_uid和st_gid只是文件所属用户和文件所属用户组的ID,并不是他们的名字,所以我们需要借助两个函数得到其名字,分别是getpwuid()和getgrgid()函数。

函数介绍如下:

函数原型函数作用
struct passwd *getpwuid(uid_t uid);通过用户的uid得到用户的数据,以passwd结构返回
uid:用户的uid(识别码)
 
struct group *getgrgid(gid_t gid);通过组的gid得到组的数据,以group结构返回
gid:组的gid(识别码)

passwd结构如下:

struct passwd 
{
    char   *pw_name;       // username         该处即为用户名
    char   *pw_passwd;     // user password 
    uid_t   pw_uid;        // user ID 
    gid_t   pw_gid;        // group ID
    char   *pw_gecos;      // user information 
    char   *pw_dir;        // home directory 
    char   *pw_shell;      // shell program 
};

group结构如下:

struct group
{
    char   *gr_name;       // group name         该处即为组名
    char   *gr_passwd;     // group password 
    gid_t   gr_gid;        // group ID /
    char  **gr_mem;        // group members 
};

获取文件所属用户名和文件所属组名程序如下所示:

struct passwd *pw;
struct group *gp;

pw = getpwuid(st.st_uid);
gp = getgruid(st.st_gid);

printf("owner name is %s \n", pw->pw_name);
printf("group name is %s \n", gp->gr_name);

下面获取文件的大小,该项就是struct stat中的st_size变量。我们直接读取其值即可。

printd("file size is %d \n", st.st_size);

最后是获取文件最后修改时间,这里我们需要利用localtime()函数将UTC时间转换成所属时区的时间信息。

struct tm *l_time;

l_time = localtime(&st.st_mtime);

printf("%2d月 %2d %2d:%2d ", l_time->tm_mon,     
                             l_time->tm_mday,
                             l_time->tm_hour,
                             l_time->tm_min);

到这里就已经用C语言将ls -l指令功能实现了。下面我们看看具体程序的效果:

图中上半部分是C语言代码实现的功能,下半部分是ls -l命令的结果。除了顺序不一样以外,结果基本一致。



        完整代码如下:

           

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

//C语言实现ll指令功能


int main(int argc, const char *argv[])
{
    int i = 0;
    DIR *p_dir = NULL;
    struct dirent *d_dir = NULL;
    struct tm *l_time = NULL;
    struct passwd *pw = NULL;
    struct group *gp = NULL;

    struct stat st;
    memset(&st, 0, sizeof(st));

    if (NULL == (p_dir = opendir(argv[1])))        //打开目录
    {
        perror("opendir");
    }
	
    while(d_dir = readdir(p_dir))                  //循环读取目录内的文件
    {
        if ('.' == *(d_dir->d_name))               //去除隐藏文件
            continue;

        stat(d_dir->d_name, &st);                  //获取文件详细信息

        switch (st.st_mode & S_IFMT)               //判断文件类型
        {
            case S_IFBLK:  printf("b");    break;  //块设备类型
					               
            case S_IFCHR:  printf("c");    break;  //字符类型
					               
            case S_IFDIR:  printf("d");    break;  //目录类型       
					               
            case S_IFIFO:  printf("p");    break;  //管道类型       
					               
            case S_IFLNK:  printf("l");    break;  //链接类型         
					               
            case S_IFREG:  printf("-");    break;  //文件类型    
					               
            case S_IFSOCK: printf("s");    break;  //套接字类型           
					              
            default:       printf("?");    break; //无法识别类型        
					               
        }

        for (i = 8; i >= 0; i--)                   //判断文件权限
        {
            if (st.st_mode & (1 << i))
            {
                switch( i % 3)
                {
                    case 2:    printf("r");    break;    //文件该处具有读权限
										               
                    case 1:    printf("w");    break;    //文件该处具有写权限
										          
                    case 0:    printf("x");    break;    //文件该处具有执行权限
                }
            }
            else
            {
                printf("-");                             //文件该处无任何权限
            }
        }


        printf(" %d ", st.st_nlink);                     //文件链接数


        pw = getpwuid(st.st_uid);                        
        gp = getgrgid(st.st_gid);
        
        printf("%s ", pw->pw_name);                      //文件所属用户名
        printf("%s ", gp->gr_name);                      //文件所属组组名



        printf("%6d ", st.st_size);                      //文件大小
	
        l_time = localtime(&st.st_mtime);                //文件最后修改时间由UTC时间转换为地区时间
        
        printf("%2d月 %2d %2d:%2d ", l_time->tm_mon + 1 , 
                                     l_time->tm_mday, 
                                     l_time->tm_hour, 
                                     l_time->tm_min);

        printf("%s \n", d_dir->d_name);                  //文件名
    }

    return 0;
}


     仓促成文,不当之处,尚祈方家和读者批评指正。联系邮箱1772348223@qq.com              
                   

 

 

 

 

 

Logo

更多推荐