ls命令实现

58 阅读3分钟

ls命令

列出文件名和文件属性,默认找出当前目录中所有文件的文件名,按字典顺序排序后输出
-l 列出文件的详细信息
-a 列出隐藏文件
-lu 显示最后访问时间
-s 以块为单位的文件大小
-t 按时间排序
-F 显示文件类型

基础功能实现思路

目录:本质是文件列表,与utmp类似,都是数据序列,记录在dirent结构体
1.打开目录
2.循环获取dirent信息
3.关闭目录

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>

void ls(char dirname[]) {
    DIR *p_dir;
    struct dirent *p_dirent;
    if((p_dir = opendir(dirname)) == NULL)
        fprintf(stderr, "open error: %s\n", dirname);
    else {
        while((p_dirent = readdir(p_dir)) != NULL) {
            printf("%s\n", p_dirent -> d_name);
        }
        closedir(p_dir);
    }
}

int main(int argc, char *argv[]) {
    if(argc == 1) {
        ls(".");
    }
    else {
        while(--argc) {
            ls(*++argv);
        }
    }
    exit(0);
}

无排序ls.png

增加按字典顺序排序功能

获取到文件名之后,将文件名放到数组中,输出前对数组进行排序后输出

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>

char *name[];
int index = 0;

void swap(int a, int b) {
    char *temp;
    temp = name[a];
    name[a] = name[b];
    name[b] = temp;
}

void mysort() {
    for(int i = 0;i < index;i ++) {
        int min_index = i;
        for(int j = i;j < index;j ++) {
            min_index = strcmp(name[min_index], name[j]) <= 0 ? min_index : j;
        }
        swap(min_index, j);
    }
}

void print_name() {
    mysort();
    for(int i = 0;i < index;i ++)
        puts(name[i]);
}

void ls(char dirname[]) {
    DIR *p_dir;
    struct dirent *p_dirent;
    if((p_dir = opendir(dirname)) == NULL)
        fprintf(stderr, "open error: %s\n", dirname);
    else {
        while((p_dirent = readdir(p_dir)) != NULL) {
            name[index++] = p_dirent -> d_name;
        }
        closedir(p_dir);
        print_name();
    }
}

int main(int argc, char *argv[]) {
    if(argc == 1) {
        ls(".");
    }
    else {
        while(--argc) {
            ls(*++argv);
        }
    }
    exit(0);
}

屏幕截图 2023-09-17 070948.png

显示详细信息

ls -l命令 显示详细信息
<模式> 文件类型和文件访问权限
<链接数> 该文件被引用的次数
<文件所有者> 文件所有者的用户名
<组> 文件所有者在的组
<大小> 实际字节数
<最后修改时间> <文件名>

获取文件属性信息

stat,在进程中创建stat结构体,调用stat函数,通知内核将文件属性放到进程的stat结构体中
man 2 stat查看结构体

stat.png

char *filename = "ls.c";
struct stat file_info;
if(stat(filename, &file_info) != -1) {

}

模式信息格式化

stat结构体中的模式信息st_mode字段是int类型,但是ls -l命令中的模式是 -rw-rw-r--这种格式

mode.png

void mode_to_str(int mode) {
    char str[10] = {0};
    strcpy(str, "----------");
    if(S_ISDIR(mode)) str[0] = 'd';
    if(S_ISCHR(mode)) str[0] = 'c';
    if(S_ISBLK(mode)) str[0] = 'b';
    if(mode & S_IRUSR) str[1] = 'r';
    if(mode & S_IWUSR) str[2] = 'w';
    if(mode & S_IXUSR) str[3] = 'x';
    if(mode & S_IRGRP) str[4] = 'r';
    if(mode & S_IWGRP) str[5] = 'w';
    if(mode & S_IXGRP) str[6] = 'x';
    if(mode & S_IROTH) str[7] = 'r';
    if(mode & S_IWOTH) str[8] = 'w';
    if(mode & S_IXOTH) str[9] = 'x';
    puts(str);
}

用户和组格式化

从stat中得到的用户和组信息都是int数值,但ls -l得到的是用户名和组名,使用getpwuid函数获取uid和用户名对应关系

getpwuid.PNG

char *uid_to_username(int uid) {
    return getpwuid(uid) -> pw_name;
}

使用getgrgid函数获取gid和组名的对应关系

getgrgid.PNG

char *gid_to_groupname(int gid) {
    return getgrgid(gid) -> gr_name;
}

时间参数格式化

void show_time(long timeval) { 
        struct tm *current_tm = localtime(&timeval); 
        printf("%d-%d-%d %d:%d:%d ", current_tm -> tm_year + 1900, current_tm -> tm_mon + 1, current_tm -> tm_mday, current_tm -> tm_hour, current_tm -> tm_min, current_tm -> tm_sec); 
}

代码实现

// ls -l的代码实现
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

char *filename[256];
int array_index = 0;

void show_time(long timeval) { 
	struct tm *current_tm = localtime(&timeval); 
	printf("%4d-%4d-%4d %2d:%2d:%2d  ", current_tm -> tm_year + 1900, current_tm -> tm_mon + 1, current_tm -> tm_mday, current_tm -> tm_hour, current_tm -> tm_min, current_tm -> tm_sec);
}

char *gid_to_groupname(int gid) {
    return getgrgid(gid) -> gr_name;
}

char *uid_to_username(int uid) {
    return getpwuid(uid) -> pw_name;
}

void mode_to_str(int mode) {
    char str[10] = {0};
	strcpy(str, "----------");
	if(S_ISDIR(mode)) str[0] = 'd';
	if(S_ISCHR(mode)) str[0] = 'c';
	if(S_ISBLK(mode)) str[0] = 'b';
	if(mode & S_IRUSR) str[1] = 'r';
	if(mode & S_IWUSR) str[2] = 'w';
	if(mode & S_IXUSR) str[3] = 'x';
	if(mode & S_IRGRP) str[4] = 'r';
	if(mode & S_IWGRP) str[5] = 'w';
	if(mode & S_IXGRP) str[6] = 'x';
	if(mode & S_IROTH) str[7] = 'r';
	if(mode & S_IWOTH) str[8] = 'w';
	if(mode & S_IXOTH) str[9] = 'x';
	printf("%s  ", str);
}

void print_stat(char *filename) {
	struct stat info;
	if(stat(filename, &info) != -1) {
		mode_to_str(info.st_mode);
		printf("%ld  ", info.st_nlink);
		printf("%s  ", uid_to_username(info.st_uid));
		printf("%s  ", gid_to_groupname(info.st_gid));
		printf("%10ld  ", info.st_size);
		show_time(info.st_mtime);
		puts(filename);
	}
}

void swap(int a, int b) {
	char *temp;
	temp = filename[a];
	filename[a] = filename[b];
	filename[b] = temp;
}

void sort() {
	for(int i = 0;i < array_index;i ++) {
		int min_index = i;
		for(int j = i;j < array_index;j ++) {
			min_index = strcmp(filename[min_index], filename[j]) <= 0 ? min_index : j;
		}
		swap(min_index, i);
	}
}

void print_filename() {
	sort();
	for(int i = 0;i < array_index;i ++)
		print_stat(filename[i]);
}

void ls(char dirname[]) {
	DIR *p_dir;
	struct dirent *p_dirent;
	if((p_dir = opendir(dirname)) == NULL) {
		fprintf(stderr, "cannot open: %s\n", dirname);
	}
	else {
		while((p_dirent = readdir(p_dir)) != NULL) {
			filename[array_index++] = p_dirent -> d_name;
		}
		closedir(p_dir);
		print_filename();
	}
}

int main(int argc, char *argv[]) {
	if(argc == 1)
		ls(".");
	else {
		while(--argc) {
			printf("%s:\n", *++argv);
			ls(*argv);
		}
	}
	exit(0);
}

ls.PNG