ls 命令实现

这个学期上Linux课,老师布置了一项作业,要我们实现ls命令。不幸,我上课没有听讲,于是,下了几天苦工,先在网上找博客,看人家的例子,看到用到的C函数库函数,再用man 命令找它的用法,尝试着把整个流程弄懂。目前我实现的ls能做 ls 和 ls -l ,其他的功能只要知道了相应的API都不是难事了,重要的是学习的过程。


相关的系统C函数主要是 readdir,opendir,closedir

相关的结构体主要是 stat,dirent,winsize (获取终端的情况时用到)


ls:简要方式,仅显示文件 and/or 目录 的名字,分栏处理,文件名按列升序排序

(列升序排序:同一行,左边的名字比右边的名字字典序小;同一列,上边的名字比下边的名字字典序小。不少Linux和Unix系统中的    ls命令都是这样显示的)


ls -l:详细方式,每行显示一个文件/目录的详细信息。

依次为:类型,权限,用户名,组名,链接数,文件大小,修改时间,文件名。


两种方式下,以'.'开头的隐藏文件均不显示( 没有 -a 参数嘛 ! )


直接给出源码:

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

void briefly(char*);
void amply(char*);
int  cmp(const void*a , const void*b);
void print_type(struct stat e);
void print_auth(struct stat e);
void print_detail(struct stat e);

//用于文件名排序
int cmp(const void*a , const void*b ) {
	return strcmp(*(char**)a,*(char**)b);
}

//简要方式 ls
void briefly(char path[]) {
	DIR* dirp;
	struct dirent *dp;	
	struct stat e;	
	//path不是合法目录,但有可能是文件
	if (( (dirp=opendir(path)) == NULL)) {
		if (lstat(path,&e) == -1) {
			printf("\033[0;31mNo such file or directory\033[0m\n");
		}
		else {
			char filename[256];
			int tag = -1, c;
			for(c = strlen(path) - 1 ; c >= 0 ; c --)
			{
				if(path[c] == '/') {
					tag = c;
					break;
				}
			}
			int b = 0;
			for(c = tag + 1 ; c < strlen(path) ; c ++)
				filename[b++] = path[c];
			printf("%s\n",filename);
		}
		return;
	}
	
	//path为目录
	int cnt = 0,ter_width;
	char * array[256];

	//获取终端列数
	struct winsize size;
	if( isatty (STDOUT_FILENO) == 0)
		exit(1);
	if(ioctl(STDOUT_FILENO,TIOCGWINSZ,&size) < 0) {
		printf("\033[0;31mioctl TIOCGWINSZ error\033[0m\n");
		printf("\033[0;31mNO such file or directory\033[0m\n");
		exit(1);
	}
	ter_width = size.ws_col;

	//获取最长文件名, 对文件名排序
	int max_len = 0;
	while( (dp = readdir(dirp)) != NULL ) {
		if(dp->d_name[0] != '.') {
			int t = strlen(dp->d_name);
			max_len = max_len < t ? t : max_len;
			array[cnt++] = dp->d_name;
		}
	}
	(void)closedir(dirp);
	qsort(array,cnt,sizeof(array[0]),cmp);

	//分栏显示
	//显示colum							  :	 floor((ter_width+2)/(max_len+2))
	//相邻两个文件名在终端显示的最小距离  :	 2 space
	//左右相邻的文件名在array中的offset   :	 ceiling(cnt/colum)
	int colum = (ter_width + 2)/(max_len + 2), offset;

	offset = cnt%colum ==0 ? cnt/colum : cnt/colum+1;
	
	//分栏输出与空格填补
	int i,j,k,p;
	for(i = 0 ; i < offset ; i ++)
	{
		j = i;
		while ( j < cnt ) {
			printf("%s",array[j]);
			if(j + offset > cnt-1 ) {
				printf("\n");
				break;
			}
			k = 0;
			p = max_len - strlen(array[j]) + 2;
			while (k < p) {
				printf(" ");
				k ++;
			}
			j += offset;
		}
	}
}

//详细方式
void amply(char path[]) {
	DIR* dirp;
	struct dirent *dp;
	struct stat e;
	//判断为文件
	if (((dirp=opendir(path)) == NULL)) {
		if(lstat(path,&e) < 0) {
			printf("\033[0;31mlstat error\033[0m\n");
			printf("\033[0;31mNo such file or directory\033[0m\n");
			return;
		}
		print_detail(e);
		int c,tag = -1;
		for(c = strlen(path) - 1 ; c >= 0 ; c --)
		{
			if(path[c] == '/') {
				tag = c;
				break;
			}
		}
		//path数组加一个offset,因为不输出目录,只输出文件名
		if(tag > 0)
			path = path+tag+1;
		printf("%s\n",path);
		return;
	}

	//判断为目录	
	while( (dp = readdir(dirp)) != NULL ) {
		if(dp->d_name[0] == '.') {
			continue;
		}
		char PATH[256];

		//拼接PATH为合法的相对路径
		strcpy(PATH,path);
		strcat(PATH,"/");
		strcat(PATH,dp->d_name);

		if(lstat(PATH,&e) < 0) {
			printf("\033[0;31mlstat error\033[0m\n");
			continue;
		}

		print_detail(e);
		printf("%s\n",dp->d_name);
	}
	(void)closedir(dirp);
}

void print_detail(struct stat e) {
		char e_time[32];
		struct passwd* pw; //存储用户名
		struct group * gp; //存储组名
		//文件类型			
		print_type(e);
		//文件权限 (所有者,同组其他用户,其他)
		print_auth(e);
		printf("  ");
		//用户名和组名
		pw = getpwuid(e.st_uid);
		gp = getgrgid(e.st_gid);
		printf("%-7s",pw->pw_name);
		printf("%-7s",gp->gr_name);
		//链接数
		printf("%-4d",e.st_nlink);
		//文件大小	
		printf("%-6d",(int)e.st_size);
		//修改时间
		strcpy(e_time, ctime(&e.st_mtime));
		e_time[strlen(e_time)-1] = '\0';
		printf("%-26s",e_time);	
}
// 打印文件类型
void print_type(struct stat e) {
	if (S_ISLNK(e.st_mode))			//是否为链接
		printf("l");
   	else if (S_ISREG(e.st_mode))	//是否为文件
   		printf("-");
   	else if (S_ISDIR(e.st_mode))	//是否为目录
   		printf("d");
   	else if (S_ISCHR(e.st_mode))	//是否为字符设备文件
   		printf("c");
   	else if (S_ISBLK(e.st_mode))	//是否为块设备文件
   		printf("b");
   	else if (S_ISFIFO(e.st_mode))	//是否为FIFO
   		printf("f");
   	else if (S_ISSOCK(e.st_mode))	//是否为socket
   		printf("s");
}

void print_auth(struct stat e) {
	//所有者权限
	if(e.st_mode & S_IRUSR)
		printf("r");
	else
		printf("-");
	if(e.st_mode & S_IWUSR)
		printf("w");
	else
		printf("-");
	if(e.st_mode & S_IXUSR)
		printf("x");
	else
		printf("-");
	//同组用户权限
	if(e.st_mode & S_IRGRP)
		printf("r");
	else
		printf("-");
	if(e.st_mode & S_IWGRP)
		printf("w");
	else
		printf("-");
	if(e.st_mode & S_IXGRP)
		printf("x");
	else
		printf("-");
	//其他
	if(e.st_mode & S_IROTH)
		printf("r");
	else
		printf("-");
	if(e.st_mode & S_IWOTH)
		printf("w");
	else
		printf("-");
	if(e.st_mode & S_IXOTH)
		printf("x");
	else
		printf("-");
}

int main(int argc, char*argv[]) {
	//ls
	if (argc == 1) {
		briefly(".");
	}
	else if (strcmp(argv[1],"-l") == 0) {
		//ls -l
		if(argc == 2)
			amply(".");
		//ls -l ...
		else {
			for(int i = 2 ; i < argc ; i ++) {
				amply(argv[i]);
			}
		}
	}
	//ls ...
	else {
		for(int i = 1 ; i < argc ; i ++)
			briefly(argv[i]);
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值