shell指令ls的实现

单文件编译,在命令行输入

cc  myls.c -o myls

即完成编译,输入./myls -l即可查看当前目录下的文件详细信息,其他选项类似。

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

#define SUBDIRSIZE 1024
#define STATBUF 1024
#define LSSHOWSIZE 1024
#define CURRENTWD 2048
#define IDBUF 1024
// 获取r w x权限
void getrwxlimits(char *buf, const struct stat *statres) {
    // owner rwx
    if ((S_IRUSR & statres->st_mode) == S_IRUSR) {
        strncat(buf, "r", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    if ((S_IWUSR & statres->st_mode) == S_IWUSR) {
        strncat(buf, "w", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    if ((S_IXUSR & statres->st_mode) == S_IXUSR) {
        strncat(buf, "x", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    // group rwx
    if ((S_IRGRP & statres->st_mode) == S_IRGRP) {
        strncat(buf, "r", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    if ((S_IWGRP & statres->st_mode) == S_IWGRP) {
        strncat(buf, "w", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    if ((S_IXGRP & statres->st_mode) == S_IXGRP) {
        strncat(buf, "x", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    // othwe rwx
    if ((S_IROTH & statres->st_mode) == S_IROTH) {
        strncat(buf, "r", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    if ((S_IWOTH & statres->st_mode) == S_IWOTH) {
        strncat(buf, "w", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
    if ((S_IXOTH & statres->st_mode) == S_IXOTH) {
        strncat(buf, "x", LSSHOWSIZE);
    } else {
        strncat(buf, "-", LSSHOWSIZE);
    }
}
// 获取硬链接数
void gethlink(char *buf, nlink_t hardlinks) {
    char hlinkbuf[512];
    sprintf(hlinkbuf, " %ld", hardlinks);
    strncat(buf, hlinkbuf, LSSHOWSIZE);
    return;
}
// 获取用户名
void getusrnam(char *buf, uid_t userid) {
    struct passwd *passline;
    // 根据uid获取passwd文件中的一行,其中包括了用户名
    passline = getpwuid(userid);
    strncat(buf, "\t", LSSHOWSIZE);
    strncat(buf, passline->pw_name, LSSHOWSIZE);
    return;
}
// 获取组名
void getgrpnam(char *buf, gid_t grpid) {
    struct group *grpline;
    // getgrgid根据gid获取group文件中的一行,其中包括了组名
    grpline = getgrgid(grpid);
    strncat(buf, "\t", LSSHOWSIZE);
    strncat(buf, grpline->gr_name, LSSHOWSIZE);
    return;
}
// 写入uid
void writeuid(char *buf,uid_t userid){
    char uidbuf[IDBUF];
    sprintf(uidbuf,"\t%d",userid);
    strncat(buf,uidbuf,LSSHOWSIZE);
}
// 写入gid
void writegid(char *buf,gid_t grpid){
    char gidbuf[IDBUF];
    sprintf(gidbuf,"\t%d",grpid);
    strncat(buf,gidbuf,LSSHOWSIZE);
}
// 获取文件大小
void getfilesize(char *buf, off_t totalsize) {
    char filesizebuf[1024];
    sprintf(filesizebuf, "\t%ld", totalsize);
    strncat(buf, filesizebuf, LSSHOWSIZE);
    return;
}
// 获取最后的修改时间
void getmoditime(char *buf, const struct timespec *moditime) {
    struct tm *tm;
    char *timebuf;
    char time_[20];
    // 将时间戳转换成"Wed Jun 30 21:49:08 1993\n"形式字符串
    timebuf = ctime(&moditime->tv_sec);
    // 因为l或n选项只显示修改月、日、时、分,所有将前面和后面的字符截断
    timebuf[strlen("Wed Jun 30 21:49")] = '\0';
    strncpy(time_, timebuf + 4, 20);
    strncat(buf, "\t", LSSHOWSIZE);
    strncat(buf, time_, LSSHOWSIZE);
}
// 参数i或n调用的函数,函数中第三个参数代表i或n选项
void paralorn(const char *path, char *lsshows,int ch) {
    struct stat statres;
    memset(lsshows, 0, LSSHOWSIZE * sizeof(char));

    if (lstat(path, &statres) < 0) {
        puts("in paral");
        puts(path);
        perror("lstat()");
        exit(1);
    }

    uid_t userid = statres.st_uid;
    gid_t groupid = statres.st_gid;
    nlink_t hardlinks = statres.st_nlink;
    off_t totalsize = statres.st_size;
    struct timespec moditime = statres.st_mtim;

    switch (statres.st_mode & S_IFMT) {
        case S_IFBLK:
            strncpy(lsshows, "b", LSSHOWSIZE);
            break;
        case S_IFCHR:
            strncpy(lsshows, "c", LSSHOWSIZE);
            break;
        case S_IFDIR:
            strncpy(lsshows, "d", LSSHOWSIZE);
            break;
        case S_IFLNK:
            strncpy(lsshows, "l", LSSHOWSIZE);
            break;
        case S_IFSOCK:
            strncpy(lsshows, "s", LSSHOWSIZE);
            break;
        case S_IFIFO:
            strncpy(lsshows, "p", LSSHOWSIZE);
            break;
        case S_IFREG:
            strncpy(lsshows, "-", LSSHOWSIZE);
            break;
        default:
            strncpy(lsshows, "?", LSSHOWSIZE);
            break;
    }

    getrwxlimits(lsshows, &statres);
    gethlink(lsshows, hardlinks);
    if (ch == 'l') {
        getusrnam(lsshows, userid);
        getgrpnam(lsshows, groupid);
    }else{
        writeuid(lsshows,userid);
        writegid(lsshows,groupid);
    }
    getfilesize(lsshows, totalsize);
    getmoditime(lsshows, &moditime);
}
// 参数i调用的参数
void parai(const char *path,char *lsshows){
    DIR *dirp;
    struct dirent *dir;
    struct stat statres;
    char inodenum[IDBUF];
    if(lstat(path,&statres)<0){
        perror("lstat()");
        exit(1);
    }

    if(S_ISDIR(statres.st_mode)){
        dirp = opendir(path);
        while ((dir = readdir(dirp))!=NULL){
            if(strcmp(dir->d_name,".") && strcmp(dir->d_name,"..")){
                memset(lsshows,0,LSSHOWSIZE);
                if(lstat(dir->d_name,&statres)<0){
                    perror("lstat()");
                    exit(1);
                }
                sprintf(inodenum,"%ld\t",statres.st_ino);
                strncpy(lsshows,inodenum,LSSHOWSIZE);
                strncat(lsshows,dir->d_name,LSSHOWSIZE);
                fprintf(stdout,"%s\t",lsshows);
            }
        }
    } else{
        sprintf(inodenum,"%ld\t",statres.st_ino);
        strncpy(lsshows,inodenum,LSSHOWSIZE);
        strncat(lsshows,path,LSSHOWSIZE);
        fprintf(stdout,"%s\t",lsshows);
    }
    puts("");
}
// 参数a调用的参数
void paraa(const char *path,char *lsshows){
    DIR *dirp;
    struct dirent *dir;
    struct stat statres;
    if(lstat(path,&statres)<0){
        perror("lstat()");
        exit(1);
    }
    if(S_ISDIR(statres.st_mode)){
        dirp = opendir(path);
        while ((dir = readdir(dirp))!=NULL){
            fprintf(stdout,"%s\t",dir->d_name);
        }
    }else{
        fprintf(stdout,"%s\t",path);
    }
    puts("");
}
// 参数l或n调用的函数
void rfileordir(const char *path, char *lsshows, int ch) {
    DIR *dirp;
    struct dirent *dir;
    struct stat statres;
    char newpath[CURRENTWD];
    // lstat获取当前文件的信息,包括文件硬链接数
    if (lstat(path, &statres) < 0) {
        puts("in rfileordir");
        perror("lstat()");
        exit(1);
    }
    if (S_ISDIR(statres.st_mode)) {
        dirp = opendir(path);
        while ((dir = readdir(dirp)) != NULL) {
            if (strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..")) {
                memset(lsshows, 0, LSSHOWSIZE);
                strncpy(newpath, path, CURRENTWD);
                strncat(newpath, "/", CURRENTWD);
                strncat(newpath, dir->d_name, CURRENTWD);
                paralorn(newpath, lsshows,ch);
                strncat(lsshows, " ", LSSHOWSIZE);
                strncat(lsshows, dir->d_name, LSSHOWSIZE);
                puts(lsshows);
            }
        }
    } else {
        paralorn(path, lsshows,ch);
        strncat(lsshows, "\t", LSSHOWSIZE);
        strncat(lsshows, path, LSSHOWSIZE);
        puts(lsshows);
    }
}
// 无任何参数时调用的函数
void nopara(const char *path, char *lsshows) {
    DIR *dirp;
    struct dirent *dir;
    // 传入一个路径,打开此路径,返回一个DIR类型的结构体
    dirp = opendir(path);
    while ((dir = readdir(dirp)) != NULL) {// 读取目录内的文件内容保存至dirent结构体
        if (strcmp(dir->d_name, ".") && strcmp(dir->d_name, ".."))
            fprintf(stdout, "%s\t", dir->d_name);
    }
    puts("");
    // 关闭目录
    closedir(dirp);
}

int main(int argc, char **argv) {
    // 存储结果数组
    char lsshows[LSSHOWSIZE];
    char currentwd[CURRENTWD];
    char *pos;
    // 获取当前工作目录(pwd的封装)
    getcwd(currentwd, CURRENTWD);
    pos = strrchr(currentwd, '/');
    int c;
    // 读取并分析命令行参数
    c = getopt(argc, argv, "-lnia");
    if(argc<2){	// 当没有命令行参数时(即只输入;一个ls时执行此项)
        nopara(currentwd, lsshows);
    }
    while (1) {
        switch (c) {	// 分析命令行参数
            case 1:
                break;
            case 'l':	// 参数l显示详细信息,以用户名、组名显示
                c = getopt(argc, argv, "-lnia");
                if (1 == c) {
                    rfileordir(argv[optind - 1], lsshows, 'l');
                } else {
                    rfileordir(currentwd, lsshows, 'l');
                }
                break;
            case 'a':	// 参数a显示当前目录下的所有文件
                c = getopt(argc,argv,"-lnia");
                if(1 == c){
                    paraa(argv[optind-1],lsshows);
                }else{
                    paraa(currentwd,lsshows);
                }
                break;
            case 'i':	// 参数i显示inode结点号
                c = getopt(argc, argv, "-lnia");
                if (1 == c) {
                    parai(argv[optind - 1], lsshows);
                } else {
                    parai(currentwd, lsshows);
                }
                break;
            case 'n':	// 参数n显示详细信息,以uid和gid显示
                c = getopt(argc, argv, "-lnia");
                if (1 == c) {
                    rfileordir(argv[optind - 1], lsshows, 'n');
                } else {
                    rfileordir(currentwd, lsshows, 'n');
                }
                break;
            default:
                break;
        }
        if (c < 0)break;
        c = getopt(argc, argv, "-lnia");
    }
    exit(0);
}

代码中使用到的函数介绍:

getcwd:获取当前工作目录(get current working directory),传入参数为一块保存当前工作目录的缓冲区,和该缓冲区的大小,当前工作目录保存到该缓冲区中。

opendir、readdir、closedri该函数族是对目录的一系列操作。

opendir:根据传入的路径名打开目录,若打开失败返回NULL,成功返回一个DIR类型的结构体指针,类似FILE流。

readdir:从DIR结构体中读取当前目录,返回dirent的结构体,该类型中放了文件的详细信息,包括文件名,直至所有的目录名被读完返回NULL。

closedir:关闭当前打开的目录。

getopt:读取并分析命令行参数,传入参数为命令行参数,以及指定要分析的字符。前面加"-“号表示读取非选项参数,若字符后边加”:"则表示可加其他参数修饰前面的选项参数。

getpwuid根据uid获取passwd文件中的一行(包含用户名)。

getgrgid根据gid获取group中的一行(包含组名)。

strftime将时间格式化成指定格式字符串。具体可查阅手册内容

ctime传入一个时间戳类型的参数,将该时间戳转换成"Wed Jun 30 21:49:08 1993\n"形式字符串。

lstat获取当前文件信息,存储到stat结构体当中,该结构体包含了文件的多项信息,包括:文件类型、文件权限、inode号、硬链接数、uid、gid……当失败时返回-1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值