Linux实现自己的ls命令-a -l -R

6 篇文章 0 订阅

目前已完成:

  • -a -l -R随机组合
  • 输出对齐
  • 屏蔽ctrl+c信号
  • 文件名排序(冒泡)
  • 在任意目录下使用
    将编译生成的文件放入/usr/bin中即可

注意

  1. 未根据文件属性加入不同的颜色
  2. 使用快排将路径名下标排序之后报段错误

输出颜色

printf("\033[字背景颜色;字体颜色m字符串\033[0m" );

//ps:
printf("\033[47;31mThis is a color test.\033[0m");

本文未对输出的字体颜色进行调整,如果有兴趣可以在本文的几个函数中加入适当的参数,稍加调整即可。

实现ls命令需要用到的几个函数

理解以下几个函数的具体作用是实现ls命令的基础

stat.h:
lstat();

dirent.h:
opendir();
readdir();
closedir();

本代码中还大量用到了字符串的复制拼接操作,如

strcpy();
strcat();

以下为源代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<linux/limits.h>
#include<dirent.h>
#include<grp.h>
#include<pwd.h>
#include<signal.h>
#include<errno.h>

#define PARAM_NONE  0
#define PARAM_A     1
#define PARAM_L     2
//#define PARAM_R     4

#define MAXROWLEN   80
#define MAXLEN    51200

int PARAM_R = 0;    //-R命令

int g_leave_len = MAXROWLEN;
int g_maxlen;

void my_err(const char *err_string, int line);
void display_dir(int flag_param, char * path);
void display(int flag, char * pathname);
void display_single(char * name);
void display_attribute(struct stat buf, char * name);
void display_R(int flag, char *filename);
void examine(int len, char *filename);
void qsort_string(char **num, int order[], int start, int finish);
int color(char *filename);
//目录蓝色  普通文件绿色


int main(int argc, char * argv[])
{
    int i, j, k, num;
    char path[PATH_MAX + 1];
    char param[32];     //保存命令行参数,目标文件名和目录名不在此列
    int flag_param = PARAM_NONE;    //参数种类,即是否有-l和-a选项
    struct stat buf;

    signal(SIGINT, SIG_IGN);    //屏蔽ctrl+c信号

    //命令行参数的解析,分析-l、-a、-al、-la选项
    j = 0;
    num = 0;
    for(i=1; i<argc; i++)
    {
        if(argv[i][0] == '-')
        {
            for(k=1; k<strlen(argv[i]); k++, j++)
            {
                param[j] = argv[i][k];  //获取-后面的参数保存到数组param中
            }
            num++;      //保存'-'的个数
        }
    }

    for(i=0; i<j; i++)
    {
        if(param[i] == 'a')
        {
            flag_param |= PARAM_A;
            continue;
        }
        else if(param[i] == 'l')
        {
            flag_param |= PARAM_L;
            continue;
        }
        else if(param[i] == 'R')
        {
            PARAM_R = 1;
            continue;
        }
        else
        {
            printf("my_ls: invalid option -%c\n", param[i]);
            exit(1);
        }
    }
    param[j] = '\0';

    //如果没有输入文件名或目录,就显示当前目录
    if((num + 1) == argc)
    {
        strcpy(path, "./");
        path[2] ='\0';
        display_dir(flag_param, path);

        return 0;
    }

    i = 1;
    do
    {
        //如果不是目标文件名或目录,就显示当前目录
        if(argv[i][0] == '-')
        {
            i++;
            continue;
        }
        else
        {
            strcpy(path, argv[i]);

            //如果目标文件或目录不存在,报错并退出程序
            if(lstat(path, &buf) == -1)
            {
                my_err("stat", __LINE__);
            }

            if(S_ISDIR(buf.st_mode))
            {
                //argv[i]是一个目录
                //如果目录的最后一个字符不是'/',就加上'/'
                if(path[strlen(argv[i]) - 1] != '/')
                {
                    path[strlen(argv[i])] = '/';
                    path[strlen(argv[i]) + 1] = '\0';
                }
                else
                {
                    path[strlen(argv[i])] = '\0';
                }

                display_dir(flag_param, path);
                i++;
            }
            else
            {
                //argv[i]是一个文件
                display(flag_param, path);
                i++;
            }
        }
    } while(i<argc);

    return 0;
}

void display_dir(int flag_param, char * path)
{
    DIR *dir;
    struct dirent *ptr;
    int count = 0;  //记录该目录下文件的数量
    char temp[PATH_MAX+1];

    //打开目录
    //获取该目录下文件总数和最长文件名
    dir = opendir(path);
    if(dir == NULL)
    {
        my_err("opendir", __LINE__);
    }
    while((ptr = readdir(dir)) != NULL)
    {
        if(g_maxlen < strlen(ptr->d_name))
        {
            g_maxlen = strlen(ptr->d_name);
        }
        count++;
    }
    //关闭目录
    closedir(dir);

    if(count > MAXLEN)
    {
        printf("too many files under this dir", __LINE__);
    }

    //获取该目录下所有文件路径名
    int i, j, len = strlen(path);
    dir = opendir(path);

    char **filenames = (char **)malloc(sizeof(char *)*MAXLEN);
    for(i=0; i<count; i++)
    {
        filenames[i] = (char *)malloc(sizeof(char)*MAXLEN);
    }
    //char filenames[MAXLEN][MAXLEN];

	//获取路径名到filenames中
    for(i=0; i<count; i++)
    {
        ptr = readdir(dir);
        if(ptr == NULL)
        {
            my_err("readdir", __LINE__);
        }
        //先获取该目录的路径
        strncpy(filenames[i], path, len);
        //examine用来修正路径名
		examine(len, filenames[i]);
		//在修正之后的路径名后拼接上文件名
		strcat(filenames[i], ptr->d_name);
		filenames[i][len+1+strlen(ptr->d_name)] = '\0';
    }


    //使用冒泡法对路径名进行排序
    //由于路径相同,因此也就是对文件名进行排序
    for(i=0; i<count-1; i++)
    {
        for(j=0; j<count-i-1; j++)
        {
            if(strcmp(filenames[j], filenames[j+1]) > 0)
            {
                strcpy(temp, filenames[j]);
                temp[strlen(filenames[j])] = '\0';
                strcpy(filenames[j], filenames[j+1]);
                filenames[j][strlen(filenames[j+1])] = '\0';
                strcpy(filenames[j+1], temp);
                filenames[j+1][strlen(temp)] = '\0';
            }
        }
    }

	//使用快排对路径名的下标进行排序
	//而不进行实际的strcpy
	/*int order[count];
	for(i=0; i<count; i++)
	{
		order[i] = i;
	}
	qsort_string(filenames, order, 0, count-1);*/
    
    //如果有-R参数,就先打印出路径名
    if(PARAM_R)
    {
        printf("\n %s :\n", path);
    }

    //int color_num;
    //输出文件名
    for(i=0; i<count; i++)
    {
        display(flag_param, filenames[i]);
    }
    /*for(i=0; i<count; i++)
    {
        //color_num = color(filenames[i]);
        display(flag_param, filenames[order[i]]);
    }*/
    printf("\n");

    closedir(dir);

    //printf("Hello!");
    //如果命令行中没有-l选项,打印一个换行符
    /*if((flag_param & PARAM_L) == 0)
    {
        //printf("Hello!");
        printf("\n");
    }*/

    //进行递归
    if(PARAM_R)
    {
        for(i=2; i<count; i++)
        {
            display_R(flag_param, filenames[i]);
        }
    }
}

//根据命令行参数和完整路径名显示目标文件
//参数 flag:命令行参数     如:-a、-l
//参数 pathname:包含了文件名的路径名
void display(int flag, char * pathname)
{
    int i,j;
    struct stat buf;    //获取文件的属性
    char name[MAXLEN];  //存储文件名

    //从路径中解析出文件名
    for(i=0,j=0; i<strlen(pathname); i++)
    {
        if(pathname[i] == '/')
        {
            j=0;
            continue;
        }
        name[j++] = pathname[i];
    }
    name[j] = '\0';
    //printf("%s ", name);
    //用lstat而不是stat以方便解析链接文件
    //lstat返回的是符号链接文件本身的状态信息

    //exit(0);
    if(lstat(pathname, &buf) == -1)
    {
        printf("pathname :%s\n",pathname);
        my_err(" stat",__LINE__);
    }

    switch(flag)
    {
        case PARAM_NONE:        //没有-l和-a选项
            if(name[0] != '.')  //如果遇到隐藏文件则直接break
            {
                display_single(name);
            }
            break;
    
    case PARAM_A:           //-a:显示包括隐藏文件在内的所有文件
        display_single(name);
        break;

    case PARAM_L:           //-l:每个文件单独占一行, 显示文件的详细属性信息
        if(name[0] != '.')  //如果遇到隐藏文件则直接break
        {
            display_attribute(buf, name);
            printf(" %-s\n", name);
        }
        break;

    case PARAM_A + PARAM_L: //同时有-a和-l选项的情况
        display_attribute(buf, name);
        printf(" %-s\n", name);
        break;

    default:
        break;
    }
}

void display_single(char * name)
{
    int i,len;

    //如果本行不足以打印一个文件名则换行
    if(g_leave_len < g_maxlen)
    {
        printf("\n");
        g_leave_len = MAXROWLEN;
    }

    len = strlen(name);
    len = g_maxlen - len;
    printf("%-s", name);
    //printf("\033[%dm%s\033[0m\t",  name); //输出自体带颜色的语法,其中%d是颜色

    for(i=0; i<len; i++)
    {
        printf(" ");
    }

    printf("  ");
    g_leave_len -= (g_maxlen + 2);
}


void display_attribute(struct stat buf, char * name)
{
    char buf_time[32];
    struct passwd *psd;         //从该结构体中获取文件所有者的用户名
    struct group *grp;          //从该结构体中获取文件所有者所属组的组名

    //获取并打印文件类型
    if(S_ISLNK(buf.st_mode)){
        printf("l");
    }
    else if(S_ISREG(buf.st_mode)){
        printf("-");
    }
    else if(S_ISDIR(buf.st_mode)){
        printf("d");
    }
    else if(S_ISCHR(buf.st_mode)){
        printf("c");
    }
    else if(S_ISBLK(buf.st_mode)){
        printf("b");
    }
    else if(S_ISFIFO(buf.st_mode)){
        printf("f");
    }
    else if(S_ISSOCK(buf.st_mode)){
        printf("s");
    }
    
    //获取并打印文件所有者的权限
    if(buf.st_mode & S_IRUSR){
        printf("r");
    }
    else{
        printf("-");
    }

    if(buf.st_mode & S_IWUSR){
        printf("w");
    }
    else{
        printf("-");
    }

    if(buf.st_mode & S_IXUSR){
        printf("x");
    }
    else{
        printf("-");
    }

    //获取并打印与文件所有者同组的用户对该文件的操作权限
    if(buf.st_mode & S_IRGRP){
        printf("r");
    }
    else{
        printf("-");
    }

    if(buf.st_mode & S_IWGRP){
        printf("w");
    }
    else{
        printf("-");
    }

    if(buf.st_mode & S_IXGRP){
        printf("x");
    }
    else{
        printf("-");
    }

    //获取并打印其他用户对该文件的操作权限
    if(buf.st_mode & S_IROTH){
        printf("r");
    }
    else{
        printf("-");
    }

    if(buf.st_mode & S_IWOTH){
        printf("w");
    }
    else{
        printf("-");
    }

    if(buf.st_mode & S_IXOTH){
        printf("x");
    }
    else{
        printf("-");
    }

    printf("    ");


    //根据uid与gid获取文件所有者的用户名与组名
    psd = getpwuid(buf.st_uid);
    grp = getgrgid(buf.st_gid);
    printf("%-4d", buf.st_nlink);    //打印文件的链接数
    printf("%-8s", psd->pw_name);
    printf("%-8s", grp->gr_name);

    printf("%6d", buf.st_size);     //打印文件的大小
    strcpy(buf_time, ctime(&buf.st_mtime));
    buf_time[strlen(buf_time) - 1] = '\0';  //去掉换行符
    printf(" %s", buf_time);                //打印文件的时间信息
}

void display_R(int flag, char *filename)
{
    //获取文件属性信息
    struct stat buf;
    lstat(filename, &buf);

    //如果是目录的话进行递归
    if(S_ISDIR(buf.st_mode))
    {
        int i,j;
        char name[MAXLEN];  //从路径名中获取文件名

        for(i=0; i<strlen(filename); i++)
        {
            if(filename[i] == '/')
            {
                j = 0;
                continue;
            }
            name[j++] = filename[i];
        }
        name[j] = '\0';

        switch(flag)
        {
            case PARAM_NONE:
                if(name[0] != '.')
                    display_dir(flag, filename);
                    break;
            case PARAM_A:
                display_dir(flag, filename);
                break;
            case PARAM_L:
                if(name[0] != '.')
                    display_dir(flag, filename);
                    break;
            case PARAM_A + PARAM_L:
                display_dir(flag, filename);
                break;
            default:
                break;
        }
    }
}

void my_err(const char *err_string, int line)
{
    fprintf(stderr, "line:%d", line);
    perror(err_string);
    exit(1);
}

int color(char *filename)
{
    struct stat buf;
    lstat(filename, &buf);

    if(S_ISREG(buf.st_mode))
    {
        return 32;      //绿色
    }
    else if(S_ISDIR(buf.st_mode))
    {
        return 34;      //蓝色
    }

    return 0;
}

void examine(int len, char *filename)
{
    if(filename[len-1] != '/')
    {
        filename[len] = '/';
        filename[len+1] = '\0';
    }
    else
    {
        filename[len] = '\0';
    }
}

//对文件名的下标进行快排
void qsort_string(char **num, int order[], int start, int finish)
{
    int left = start;
    int right = finish;
    int duff;
    if(start < finish)
    {
        duff = order[start];
        while(start < finish && strcmp(num[order[finish]], num[duff]) >= 0)
        {
            finish--;
        }
        order[start] = order[finish];
        while(start < finish && strcmp(num[order[start]], num[duff]) <= 0)
        {
            start++;
        }
        order[finish] = order[start];
    }
    if(start == finish)
    {
        order[finish] = duff;
    }
    qsort_string(num, order, finish+1, right);
    qsort_string(num, order, left, finish-1);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值