LINUX系统编程_实现简单的ls命令

不定期补充、修正、更新;欢迎大家讨论和指正
以下所用的函数只给出基本用法,想更进一步了解可以看看下面的博客或者查阅man手册
Linux系统编程_文件和系统属性

初始

先实现最简单的,利用stat函数获取单个文件信息,最终希望实现下面这种效果

在这里插入图片描述

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

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

statbuf
	   struct stat {
               dev_t     st_dev;         /* 块设备号 */
               ino_t     st_ino;         /* Inode结点号 */
               mode_t    st_mode;        /* 文件类型和文件权限 */
               nlink_t   st_nlink;       /* 硬链接数 */
               uid_t     st_uid;         /* 文件所属用户ID*/
               gid_t     st_gid;         /* 文件所属组ID */
               dev_t     st_rdev;        /* 字符设备ID */
               off_t     st_size;        /* 文件大小 */
               blksize_t st_blksize;     /* Block size for filesystem I/O */
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

               struct timespec st_atim;  /* 最后文件访问时间 */
               struct timespec st_mtim;  /* 最后修改文件内容时间 */
               struct timespec st_ctim;  /* 最后修改文件属性时间 */

在这里插入图片描述
效果如下,还十分简陋。
在这里插入图片描述

改进文件类型和权限部分

我们一步步改进,首先先改进文件类型和权限部分
目前显示的是33188,转换为二进制为‭1000000110100100‬,我们将其分为4组,‭1000/000/110/100/100‬
第一组文件类型,第二组代表设置位,就是SUID SGID, Sticky.后三组就是我们熟悉的所属用户,所属组,其他人的读写执行权限了。
这时我们就可以利用st_mode&0170000八进制(1111000000000000)提取前四位的信息,如果为0100000(1000 后面省略若干0)则为普通文件
好在<stat.h>将这些值宏定义,可以方便的调用,同理在权限部分也用这种方法,宏定义如下
	S_IFMT 	   0170000 	  文件类型的位遮罩

	S_IFSOCK   0140000    socket

	S_IFLNK    0120000    符号链接(symbolic link)

	S_IFREG    0100000    一般文件

	S_IFBLK    0060000    区块装置(block device)

	S_IFDIR    0040000    目录

	S_IFCHR    0020000    字符装置(character device)

	S_IFIFO    0010000    先进先出(fifo)

	S_ISUID    0004000    文件的(set user-id on execution)位

	S_ISGID    0002000    文件的(set group-id on execution)位

	S_ISVTX    0001000    文件的sticky位

	S_IRWXU    00700      文件所有者的遮罩值(即所有权限值)

	S_IRUSR    00400      文件所有者具可读取权限

	S_IWUSR    00200      文件所有者具可写入权限
	
	S_IXUSR    00100      文件所有者具可执行权限

	S_IRWXG    00070      用户组的遮罩值(即所有权限值)

	S_IRGRP    00040      用户组具可读取权限

	S_IWGRP    00020      用户组具可写入权限

	S_IXGRP    00010      用户组具可执行权限

	S_IRWXO    00007      其他用户的遮罩值(即所有权限值)
	
	S_IROTH    00004      其他用户具可读取权限

	S_IWOTH    00002      其他用户具可写入权限
	
	S_IXOTH    00001      其他用户具可执行权限

带参宏
		S_ISREG(m)    is it a regular file?
        S_ISDIR(m)    directory?
        S_ISCHR(m)    character device?
        S_ISBLK(m)    block device?
        S_ISFIFO(m)   FIFO (named pipe)?
        S_ISLNK(m)    symbolic link? (Not in POSIX.1-1996.)
        S_ISSOCK(m)   socket? (Not in POSIX.1-1996.)

成功改进文件类型的显示

在这里插入图片描述
在这里插入图片描述

文件权限的改进:这里通过移位操作来逻辑运算

在这里插入图片描述
在这里插入图片描述

改进用户名和组名

使用getpwuid和getgrgid函数即可实现
SYNOPSIS   
  
       struct passwd *getpwuid(uid_t uid);
      
struct passwd {
               char   *pw_name;       /* 用户名 */
               char   *pw_passwd;     /* 是否有密码 */
               uid_t   pw_uid;        /* 用户ID */
               gid_t   pw_gid;        /* 组ID */
               char   *pw_gecos;      /* 注释*/
               char   *pw_dir;        /* 用户家目录 */
               char   *pw_shell;      /* shell程序 */
           };

SYNOPSIS
      
       struct group *getgrgid(gid_t gid);

struct group {
               char   *gr_name;        /* 组名 */
               char   *gr_passwd;      /* 是否有组密码 */
               gid_t   gr_gid;         /* 组ID */
               char  **gr_mem;         /* 指向组成员用户名的指针数组 */
           };

在这里插入图片描述
在这里插入图片描述

改进时间

时间函数有许多种,这里用gmtime即可
 		struct tm *gmtime(const time_t *timep);
 		
 		struct tm {
               int tm_sec;    /* Seconds (0-60) */
               int tm_min;    /* Minutes (0-59) */
               int tm_hour;   /* Hours (0-23) */
               int tm_mday;   /* Day of the month (1-31) */
               int tm_mon;    /* Month (0-11) */
               int tm_year;   /* Year - 1900 */
               int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
               int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
               int tm_isdst;  /* Daylight saving time */
           };
这里的时间是访问时间,ll出来的是创建时间,把函数内的atime改为ctime即可,这里就不更正了

在这里插入图片描述
在这里插入图片描述

改进符号链接文件

当文件类型是符号链接时,可以看到自己实现的ls有很大的错误,这是因为ll查看的是符号链接文件本身
而myls查看的是符号链接指向的文件属性
一部分错误只需将stat函数改为lstat函数即可

在这里插入图片描述

使用readlink函数读取符号链接文件指向背后的文件
剩下的就是排版问题,顺便把第一行的内容注释了。
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);

在这里插入图片描述
在这里插入图片描述

打印多条文件项

目前只能打印某一指定的文件路径,而ll的路径是目录时,会显示目录下所有的文件的信息
所以需要用opendir和readdir函数改进,当指定的路径时目录时,打印其下的所有文件,当指定路径为其他文件,正常输出。

在这里插入图片描述


opendir函数

       DIR *opendir(const char *name);
       DIR *fdopendir(int fd);
       

	
 readdir函数
	  
	struct dirent *readdir(DIR *dirp);
 	
 	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 */
           };

在这里插入图片描述
在这里插入图片描述

将打印单个文件内容的函数封装成display_file函数,在readdir调用
  • 占坑

完整代码

#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<dirent.h>
#include<errno.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
int main(int argc,char **argv){

        struct stat statbuf={0};
        if((lstat(argv[1],&statbuf))==-1){
                perror("stat fail");
                exit(1);
        }
        /*
        printf("%d %ld %d %d %ld %ld %s\n",
                        statbuf.st_mode,
                        statbuf.st_nlink,
                        statbuf.st_uid,
                        statbuf.st_gid,
                        statbuf.st_size,
                        statbuf.st_atime,
                        argv[1]);
        */    
         switch (statbuf.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;
           }
           //打印文件类型
         char f_mode[10]={0};
         char tmp_buf[]="rwxrwxrwx";
         for(int i=0;i<9;i++)
         {
                 if(statbuf.st_mode & (1<<(8-i)))
                         f_mode[i]=tmp_buf[i];
                 else
                         f_mode[i]='-';
         }
         printf("%s ",f_mode);
			//打印文件权限
         printf("%ld ",statbuf.st_nlink);
			//打印硬链接数
         struct passwd *uid=getpwuid(statbuf.st_uid);
         struct group  *gid=getgrgid(statbuf.st_gid);
         printf("%s %s\t",uid->pw_name,gid->gr_name);
			//打印所有者和所属组
         printf("%8ld ",statbuf.st_size);
         struct tm *t=gmtime(&(statbuf.st_atime));
         printf("%d月  %d %d:%d ",t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min);
			//打印访问日期
         if((statbuf.st_mode& S_IFMT)==S_IFLNK){
                 char sym_buf[50]={0};
                 readlink(argv[1],sym_buf,sizeof(sym_buf));
                 printf("%s -> %s",argv[1],sym_buf);
         }
         else
                 printf("%s",argv[1]);
             //打印文件名
        printf("\n");
        return 0;

}



版权声明:本文为weixin_44568462原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。