Linux下實現自己的ls命令

主要功能

1.實現-l,-a,-R,-r
2.實現按文件類型以不同顏色顯示

主要知識

1.各種有關文件的系統調用函數
函數具體內容以及用法詳情參考Linux_c編程實戰第六章
2.運用二進制的思想來儲存與判斷參數
對於一個參數來講只有兩種結果:存在或者不存在。因此我們可以用一個二進制數的一位來表示一個參數,1代表存在,0代表不存在。因此在代碼中使用了四位的二進制數來表示四種參數,0001(第一位)0010(第二位)0100(第三位)1000(第四位)分別表示參數a,l,R,r的存在。
3.遞歸的應用
因爲R參數要求打印目錄以及其所有子目錄的文件信息,對於每一個目錄文件來說都要打印其文件信息,也就是在所有目錄文件下都要做同一件事情。因此採用遞歸來實現。(使用棧也可)
4.打印指定顏色字體
格式

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

部分顏色代碼:
字背景顏色: 40–49 字顏色: 30–39
40: 黑 30: 黑
41: 紅 31: 紅
42: 綠 32: 綠
43: 黃 33: 黃
44: 藍 34: 藍
45: 紫 35: 紫
46: 深綠 36: 深綠
47:白色 37:白色

具體代碼以及註釋

my_ls.h

#ifndef _MY_LS_H
#define _MY_LS_H

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/limits.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
//採用二進制思想解析參數
#define PARAM_NONE 0
#define PARAM_A 1
#define PARAM_L 2
#define PARAM_R 4
#define PARAM_r 8
#define MAXROWLEN 200
#define NORMAL 0
#define GREEN  1 
#define BLUE   2
#define S_BLUE 3
#define YELLOW 4
int g_leave_len =MAXROWLEN;
int g_maxlen;
void display_dir(int flag_p,char *path);
void display(int flag,char *pathname,int color);
void print_fname(char *name,int color);
void print_finfo(struct stat buf,char *name);
void my_err(const char *str,int line);
void display_Subdir(int flag_p,int j,char **dirs);
int display_color(struct stat buf);
void print_color(char *name,int color);

#endif

my_ls.c

#include <stdio.h>
#include "my_ls.h"
int main(int argc,char *argv[])
{
    int k=0;
    int num=0;
    char param[32];
    int flag_p=PARAM_NONE;
    //記錄參數
    for(int i=1;i<argc;i++){
        if(argv[i][0]=='-'){
            for(int j=1;j<strlen(argv[i]);j++){
                param[k]=argv[i][j];
                k++;
            }
        num++;//記錄選項參數個數,方便確認是否指定目錄
        }
    }
    //將參數轉化爲數字標記
    for(int i=0;i<k;i++){
        if(param[i]=='a'){
            flag_p|=PARAM_A;
            continue;
        }else if(param[i]=='l'){
            flag_p|=PARAM_L;
            continue;
        }else if(param[i]=='R'){
            flag_p|=PARAM_R;
            continue;
        }else if(param[i]=='r'){
            flag_p|=PARAM_r;
            continue;
        }else{
            printf("參數錯誤");
        }
    }
    param[k]=0;
    char path[PATH_MAX+1];//儲存目錄名
    //若未指定目錄則執行
    if(num==argc-1){
        strcpy(path,"./");
        path[2]=0;
        display_dir(flag_p,path);
        if((flag_p & PARAM_R)!=0 && (flag_p & PARAM_L)==0){
            printf("\n");
        }
        return 0;
    }
    //若參數中含有目錄項
    int i=1;
    struct stat buf;    
    do{
        if(argv[i][0]=='-'){
            i++;
            continue;
        }else{
            strcpy(path,argv[i]);
        }
        if(stat(path,&buf)==-1)
            my_err("stat",__LINE__);
        if(S_ISDIR(buf.st_mode)){
            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_p,path);
            i++;
         }
        else{
            display(flag_p,path,0);
            i++;
        }
    }while(i<argc);
    if((flag_p & PARAM_R)!=0 && (flag_p & PARAM_L)==0){
        printf("\n");
    }
    return 0;
}
void display_dir(int flag_p,char *path)
{
    //打開目錄
    DIR *dir;
    if((dir=opendir(path))==NULL){
        my_err("opendir",__LINE__);
    }
    //獲取該目錄下文件總數和最長文件名的長度
    struct dirent *ptr;
    int count=0;
    while((ptr=readdir(dir))!=NULL){
        if(g_maxlen<strlen(ptr->d_name)){
            g_maxlen=strlen(ptr->d_name);
        }
        count++;
    }
    if(count>20000){
        my_err("too many files under this directory",__LINE__);
    }
    closedir(dir);
    //獲取該目錄下所有文件名並與其路徑名儲存
    char **f_names;
    f_names=(char **)malloc(sizeof(char *)*20000);
    for(int i=0;i<20000;i++){
        f_names[i]=(char *)malloc(PATH_MAX+1);
    }
    int path_len=strlen(path);
    if((dir=opendir(path))==NULL){
        my_err("opendir",__LINE__);
    } 
    for(int i=0;i<count;i++){
        ptr=readdir(dir);
        strncpy(f_names[i],path,path_len);
        f_names[i][path_len]=0;
        strcat(f_names[i],ptr->d_name);
        f_names[i][path_len+strlen(ptr->d_name)]=0;
    }
    closedir(dir);
    //使用冒泡法對文件名排序
    char temp[PATH_MAX+1];
    for(int i=0;i<count-1;i++){
        for(int j=0;j<count-1-i;j++){
            if(strcmp(f_names[j],f_names[j+1])>0){
                strcpy(temp,f_names[j]);
                temp[strlen(f_names[j])]=0;
                strcpy(f_names[j],f_names[j+1]);
                f_names[j][strlen(f_names[j+1])]=0;
                strcpy(f_names[j+1],temp);
                f_names[j+1][strlen(temp)]=0;
            }
        }
    }
    //遍歷並調用函數打印文件
    char **dirs;
    dirs=(char **)malloc(sizeof(char *)*20000);
    for(int i=0;i<20000;i++){
        dirs[i]=(char *)malloc(PATH_MAX+1);
    }
    struct stat buf;
    int j=0;
    //若有-R選項先打印路徑名
    if((flag_p & PARAM_R)!=0){
        printf("%s:\n",path);
    }
    //將行剩餘空間設置爲最大值
    g_leave_len=MAXROWLEN;
    if((flag_p & PARAM_r)==0){
        for(int i=0;i<count;i++){
            //如果參數含有-R選項
            if((flag_p & PARAM_R)!=0){
                //獲取文件名
                int k=0;
                char name[NAME_MAX+1];
                for(int a=0;a<strlen(f_names[i]);a++){
                    if(f_names[i][a]=='/'){
                        k=0;
                        continue;
                    }
                    name[k]=f_names[i][a];
                    k++;
                }
                name[k]=0;
                //記錄目錄文件
                memset(&buf,0,sizeof(buf));
                if(lstat(f_names[i],&buf)==-1){
                    my_err("lstat",__LINE__);
                }
                if(S_ISDIR(buf.st_mode)){
                    if(name[0]!='.'){
                        strcpy(dirs[j],f_names[i]);
                        if(dirs[j][strlen(dirs[j])-1]!='/'){
                            dirs[j][strlen(dirs[j])]='/';
                            dirs[j][strlen(dirs[j])+1]=0;
                        }
                        j++;
                    }
                }
            }
            memset(&buf,0,sizeof(buf));
            if(lstat(f_names[i],&buf)==-1){
                my_err("lstat",__LINE__);
            }
            int color=display_color(buf);
            display(flag_p,f_names[i],color);
        }
    }
    if((flag_p & PARAM_r)!=0){
        for(int i=count-1;i>=0;i--){
            //如果參數含有-R選項
            if((flag_p & PARAM_R)!=0){
                //獲取文件名
                int k=0;
                char name[NAME_MAX+1];
                for(int a=0;a<strlen(f_names[i]);a++){
                    if(f_names[i][a]=='/'){
                        k=0;
                        continue;
                    }
                    name[k]=f_names[i][a];
                    k++;
                }
                name[k]=0;
                //記錄目錄文件
                memset(&buf,0,sizeof(buf));
                if(lstat(f_names[i],&buf)==-1){
                    my_err("lstat",__LINE__);
                }
                if(S_ISDIR(buf.st_mode)){
                    if(name[0]!='.'){
                        strcpy(dirs[j],f_names[i]);
                        if(dirs[j][strlen(dirs[j])-1]!='/'){
                            dirs[j][strlen(dirs[j])]='/';
                            dirs[j][strlen(dirs[j])+1]=0;
                        }
                        j++;
                    }
                }
            }
            memset(&buf,0,sizeof(buf));
            if(lstat(f_names[i],&buf)==-1){
                my_err("lstat",__LINE__);
            }
            int color=display_color(buf);
            display(flag_p,f_names[i],color);
        }
    }
    //若參數中有-R選項
    if((flag_p & PARAM_R)!=0){
            display_Subdir(flag_p,j,dirs);
    }
    if((flag_p & PARAM_L)==0 && (flag_p & PARAM_R)==0){
        printf("\n");
    }
    free(f_names);
    free(dirs);
}
void display(int flag,char *pathname,int color)
{
    //從路徑名中解析出文件名
    int j=0;
    char name[NAME_MAX+1];
    for(int i=0;i<strlen(pathname);i++){
        if(pathname[i]=='/'){
            j=0;
            continue;
        }
        name[j++]=pathname[i];
    }
    name[j]=0;
    //獲取文件信息並儲存
    struct stat buf;
    if(lstat(pathname,&buf)==-1){
        my_err("lstat",__LINE__);
    }
    //根據命令參數調用不同函數打印文件信息
    flag=flag & (~PARAM_r);
    switch(flag){
        case PARAM_NONE :
            if(name[0]!='.'){
                print_fname(name,color);
            }
            break;
        case PARAM_A :
            print_fname(name,color);
            break;
        case PARAM_L :
            if(name[0]!='.'){
                print_finfo(buf,name);
                printf("  ");
                print_color(name,color);
                printf("\n");
            }
            break;
        case PARAM_R :
            if(name[0]!='.'){
                print_fname(name,color);
            }
            break;
        case PARAM_A+PARAM_L :
            print_finfo(buf,name);
            printf("  ");
            print_color(name,color);
            printf("\n");
            break;
        case PARAM_A+PARAM_R :
            print_fname(name,color);
            break;
        case PARAM_L+PARAM_R :
            if(name[0]!='.'){
                print_finfo(buf,name);
                printf("  ");
                print_color(name,color);
                printf("\n");
            }
            break;
        case PARAM_A+PARAM_L+PARAM_R :
            print_finfo(buf,name);
            printf("  ");
            print_color(name,color);
            printf("\n");
            break;
    }
}
void print_fname(char *name,int color)
{
    //判斷本行是否有充足空間打印
    if(g_leave_len<g_maxlen){
        printf("\n");
        g_leave_len=MAXROWLEN;
    }
    //對齊輸出
    int len=g_maxlen-strlen(name);
    print_color(name,color);
    for(int i=0;i<len;i++){
        printf(" ");
    }
    printf("  ");
    //實時記錄行剩餘空間
    g_leave_len-=(g_maxlen+2);
}
void print_finfo(struct stat buf,char *name)
{
    //打印文件類型和文件訪問權限
    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("  ");
    //打印硬鏈接數
    printf("%4ld ",buf.st_nlink);
    //打印文件所有者以及所在組
    struct passwd *psd=psd=getpwuid(buf.st_uid);//id轉爲名字
    struct group *grp=getgrgid(buf.st_gid);
    printf("%-8s",psd->pw_name);
    printf("%-8s",grp->gr_name);
    //打印文件大小
    printf("%6ld",buf.st_size);
    //打印文件創建時間
    char buf_time[32];
    strcpy(buf_time,ctime(&buf.st_mtime));
    buf_time[strlen(buf_time)-1]=0;
    printf("   %s",buf_time);
}
void display_Subdir(int flag_p,int j,char **dirs)
{
    for(int i=0;i<j;i++){
        if((flag_p & PARAM_L)==0){
            printf("\n");
        }
        display_dir(flag_p,dirs[i]);
    }
}
int display_color(struct stat buf)
{
    int color=0;
    if(S_ISDIR(buf.st_mode)) {
        color = BLUE;
    }
    if((buf.st_mode & S_IXUSR) && color != BLUE) {
        color = GREEN;
    }
    return color;
}
void print_color(char *name,int color)
{
    if(color == GREEN) {
        printf("\033[1m\033[32m%-s\033[0m",name);
    } else if(color == BLUE){
        printf("\033[1m\033[34m%-s\033[0m",name);
    } else if(color == NORMAL){
        printf("%-s",name);
    }
}
void my_err(const char *str,int line)
{
    fprintf(stderr,"line:%d",line);
    perror(str);
    exit(1);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章