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;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章