用linux系统调用实现类似终端的ll命令

用linux系统调用实现类似终端的ll命令

一、思路分析

在这里插入图片描述

在终端输入ll会在终端打印该目标文件夹下的所有文件

获得的信息如下:

  1. 每个文件的信息独占一行
  2. 文件的类型、权限、所属用户,创建时间等都是需要用到stat()函数的
  3. 目录的颜色、可执行文件的颜色及其后字符均与其他文件不一样
  4. 链接文件是有箭头指向源文件的

了解 linux 的目录接口函数

在此,会用到三个结构体(或者说是三个主要的函数):

  • typedef struct __dirstream DIR;
  • struct dirent
  • struct stat

对应的三个函数原型是:

DIR *opendir(const char *name);	
struct dirent *readdir(DIR *dirp);
int stat(const char *pathname, struct stat *statbuf);

想要获取某目录下(比如include目录下)stdio.h文件的详细信息,我们应该怎样做?

  1. 首先,我们使用opendir函数打开目录include,返回指向目录include的DIR结构体pf。
  2. 接着,我们调用readdir(pf)函数读取目录include下所有文件(包括目录),返回指向目录include下所有文件的dirent结构体d。
  3. 然后,我们遍历d,调用stat(d->name,sta)来获取每个文件的详细信息,存储在stat结构体sta中。
    总体就是这样一种逐步细化的过程,在这一过程中,三种结构体扮演着不同的角色。
//DIR 结构体
struct __dirstream   
{   
    void *__fd;    
    char *__data;    
    int __entry_data;    
    char *__ptr;    
    int __entry_ptr;    
    size_t __allocation;    
    size_t __size;    
    __libc_lock_define (, __lock)    
}; 

struct dirent	//目录内容
{
   long d_ino; 					/* inode number 索引节点号 */
   off_t d_off; 				/* offset to this dirent 在目录文件中的偏移 */
   unsigned short d_reclen; 	/* length of this d_name 文件名长 */
   unsigned char d_type; 		/* the type of d_name 文件类型 */
   char d_name [NAME_MAX+1]; 	/* file name (null-terminated) 文件名,最长255字符 */
}

struct stat		//文件的详细信息
{   
    dev_t       st_dev;     /* ID of device containing file -文件所在设备的ID*/  
    ino_t       st_ino;     /* inode number -inode节点号*/    
    mode_t      st_mode;    /* protection -文件访问权限*/    
    nlink_t     st_nlink;   /* number of hard links -链向此文件的连接数(硬连接)*/    
    uid_t       st_uid;     /* user ID of owner -user id*/    
    gid_t       st_gid;     /* group ID of owner - group 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 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 - 创建时间*/    
};

二、代码实现

2.1 打开目录流

在此为了方便,利用main函数的命令行参数去打开对应路径

int main(int argc, char *argv[])
{
    @msg
        argc : 是命令行参数的个数
        *argv[] : 是命令行参数的地址
}
// 而在此只需要一个参数,因此取argv[1],即可。

打开目录流

int main(int argc, char *argv[])
{
    printf("open %s\n",argv[1]);
    DIR *pdir = opendir(argv[1]);
    if(!pdir)
    {
        perror("open dir");
        return -1;
    }
}

2.2 获取目录下的文件

//先定义一个dirent结构体指针去接收readdir的返回值,并将目录流指针传入readdir函数中。
struct dirent *file;
file = readdir(pdir)
{
	@return
        dirent *:失败返回NULL
}

因为要读取目录下的所有文件,因此要在一个循环中完成,一般是如下:

struct dirent *file;
while(file = readdir(pdir))
{
    //要干的事情。
}

2.3 获取每个文件的详细信息

同理也是要先定义一个stat结构体变量,使其获取文件属性信息。

struct stat _sta, *sta = &_sta;
int stat(const char *pathname, struct stat *buf);
{
    @msg
        *pathname:路径及其文件名
        *buf:获取到的文件属性信息就记录在 struct stat 结构体中 。
    @return
        int :失败返回-1.并设置errorno。
}

现在的问题就是如和获取到该目录下的所有文件的路径及其文件名。

路径已经知道,就是main的第一个命令行参数argv[1],文件名就是dirent结构体下的d_name成员。

因此获得完整的路径和文件名就很简单了:使用sprintf或strcat函数皆可。

在这里简单使用一下fprintf函数:

struct dirent *file;
char path[300];		//因为在dirent结构体中d_name成员的长度有255.
struct stat _sta, *sta = &_sta;		//定义stat结构体
while(file = readdir(pdir))
{
    sprintf(path,"%s/%s",argv[1],(char *)file->d_name);	//将路径和文件名用/拼接起来
    //此时的path就是完整的路径和文件名了。
    ret_stat = stat(path, sta);
	if(ret_stat == -1)
	{
        perror("stat");
		return -1;
	}

    //在此每个文件的详细信息就获取到了stat结构体定义的_sta变量中了。
}

2.4 获取文件的类型和读改写权限

在stat结构体的st_mode下有给我们的信息很多,因此在获取权限和文件类型时,需要屏蔽其他干扰位,最简单的办法就是使用按位与,其功能是与0为0,与1不变。

//获取文件的类型

char *pl[]={"s","l","-","b","d","c","p","?"};
char *type(int a) //获得类型的函数
{
	switch(a)
	{
		case 014:return pl[0];
		case 012:return pl[1];
		case 010:return pl[2];
		case 006:return pl[3];
		case 004:return pl[4];
		case 002:return pl[5];
		case 001:return pl[6];
		default:
			   return pl[7];
	}
}
mode_t var_type = (sta->st_mode & 0170000) >> 12;

//获取文件的权限
char *p[]={"---","--x","-w-","r--","-wx","rw-","r-x","rwx","err"};
char *chomd(int a)//获得权限的函数
{
	switch(a)
	{
		case 0:return p[0];
		case 1:return p[1];
		case 2:return p[2];
		case 3:return p[4];
		case 4:return p[3];
		case 5:return p[6];
		case 6:return p[5];
		case 7:return p[7];
		default:
			   return p[8];
	}
}
chomd((sta->st_mode & 0070)>>3);	//调用时通过右移来确定哪位用户的权限。

2.5 获取文件的所属用户和用户组

在stat结构体定义了st_uid和st_gid,这两个是用户的id,而不是用户的名称。需要使用**getpwuid()getgrgid()**函数通过id获取到名称

getpwuid(sta->st_uid)->pw_name;	//所属用户
getgrgid(sta->st_gid)->gr_name;	//用户组

2.6 获取文件的大小

stat结构体下的st_size表示的就文件的大小

2.7 转换文件的创建时间

st_mtime的时间是自1900.1.1至今的秒数,在c库中有一个函数即可将时间转换为日常生活中的风格。

#include <time.h>
struct tm *localtime(const time_t * calptr); 	
// 将时间数值变换成本地时间,考虑到本地时区和夏令时标志;   
    
struct tm {
	int tm_sec;       /* 秒 – 取值区间为[0,59] */ 
	int tm_min;       /* 分 - 取值区间为[0,59] */ 
	int tm_hour;      /* 时 - 取值区间为[0,23] */ 
	int tm_mday;     /* 一个月中的日期 - 取值区间为[1,31] */ 
	int tm_mon;     /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */ 
	int tm_year;     /* 年份,其值等于实际年份减去1900 */ 
	int tm_wday;    /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一 */ 
	int tm_yday;    /* 从每年1月1日开始的天数– 取值区间[0,365],其中0代表1月1日 */ 
	int tm_isdst;    /* 夏令时标识符,夏令时tm_isdst为正;不实行夏令时tm_isdst为0 */    
};

2.8 文件名的不同颜色

printf()函数在Linux下的功能极其强大,它可以用以些特殊符号让字符在终端显示不同的颜色,包括粗体、斜体、背景色、前景色等。

int printf ( const char * format, ... );

//其一般用法为
 printf("hello ");

//只需要在双引号内加上如下转义
		"\e[?;?;?m "		//问号表示数字,以 \e[ 开头,以m结尾,参数用分号分隔,可缺省为一个参数
 printf("\e[33mhello ");

  字背景颜色范围:40----49
  40:41:深红
  42:绿
  43:黄色
  44:蓝色
  45:紫色
  46:深绿
  47:白色

  字颜色:30-----------39
  30:31:32:绿
  33:34:蓝色
  35:紫色
  36:深绿 
  37:白色
  0:默认

三、完整代码

/**************************************************************
 * File Name     : ll.c
 * Creat Time    : 2022年11月02日 星期三 18时33分42秒
 * 备注          : 
 ***************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

char *p[]={"---","--x","-w-","r--","-wx","rw-","r-x","rwx","err"};
char *pl[]={"s","l","-","b","d","c","p","?"};
char *type(int a)
{
	switch(a)
	{
		case 014:return pl[0];
		case 012:return pl[1];
		case 010:return pl[2];
		case 006:return pl[3];
		case 004:return pl[4];
		case 002:return pl[5];
		case 001:return pl[6];
		default:
			   return pl[7];
	}
}
char *chomd(int a)
{
	switch(a)
	{
		case 0:return p[0];
		case 1:return p[1];
		case 2:return p[2];
		case 3:return p[4];
		case 4:return p[3];
		case 5:return p[6];
		case 6:return p[5];
		case 7:return p[7];
		default:
			   return p[8];
	}
}
int print(char *str, int mode, char *typ)
{	
	if(*typ == '-')
	switch(mode)
	{
		case 3:;
		case 2:;
		case 1:return printf("\e[1;32m%s\e[0m*\n",str);
		case 0:return printf("%s\n",str);
		default:return -1;
	}
	else if(*typ =='d')
		return printf("\e[1;34m%s\e[0m/\n",str);
	return 0; 
}
int main(int argc, char *argv[])
{
	printf("open %s\n",argv[1]);
	DIR *pdir = opendir(argv[1]);
	if(!pdir)
	{
		perror("open dir");
		return -1;
	}
	struct dirent *file;
	struct stat _sta, *sta = &_sta;
	int ret_stat = 1, mode_tmp = 0, count = 0;
	time_t now;
	struct tm *date;
	char path[300], linkbuf[300];
	while(file = readdir(pdir))
	{
		sprintf(path,"%s/%s",argv[1],(char *)file->d_name);
		ret_stat = stat(path, sta);
		if(ret_stat == -1)
		{
			return -1;
		}
		mode_t tmp = sta->st_mode & 0777;
		mode_t var_type = (sta->st_mode & 0170000) >> 12;
		date = localtime(&sta->st_mtime);
		printf("%s%s%s%s",
				type(var_type),
				chomd((sta->st_mode & 0700)>>6),
				chomd((sta->st_mode & 0070)>>3),
				chomd(sta->st_mode & 0007));
		printf("%3ld%6s%6s%8ld%3d月%3d %02d:%02d ",
				sta->st_nlink,
				getpwuid(sta->st_uid)->pw_name,
				getgrgid(sta->st_gid)->gr_name, 
				sta->st_size,
				date->tm_mon+1,
				date->tm_mday,
				date->tm_hour,
				date->tm_min
			  );
		mode_tmp = (tmp & 1) + ((tmp & 0100)>>6) + ((tmp & 0010) >> 3);
		print(file->d_name, mode_tmp, type(var_type));
		count++;
		//printf("\n\n%ld\n",file->d_off);
		fflush(stdout);
		usleep(100000);
	}
	printf("\n共%d个文件\n",count);
	closedir(pdir);
	return 0;
}

四、效果

在这里插入图片描述

五、总结

总的来说,该例是要一个要熟练理解有关目录和文件的三个结构体的用途,并通过man手册查阅对应函数作用和用法,就能得到较高的效率。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值