C语言实现简单的minishell

探索开源项目:MiniShell

引言

在计算机编程的世界里,Shell 是一个至关重要的组成部分,它允许用户与操作系统交互,执行命令和程序。MiniShell 是一个简化版的 Shell 程序,通常用于教学和学习目的。在本文中,我们将深入分析一个 MiniShell 的实现代码,并探讨其功能和潜在的应用场景。

简单的命令解释器minishell 

Minishell项目的功能,主要就是实现命令的运行。

涉及命令如下:

1,cp  复制文件 cp 1 2 把文件1复制成文件2

2,cat 查看文件 cat 1  查看文件到内容

3,cd  切换路径 cd 1   切换到目录1中      //chdir 

4,ls  查看当前目录下到文件 ls 或 ls /home   

5,ll  查看当前目录下到文件 ll 或 ll /home    ls -l  

6,touch 新建文件  touch 1  新建文件 1  

7,rm删除文件 

Minishell的业务流程:

  1. 运行程序 ,打印提示符

linux@ubuntu:~$    

用户名@主机名:当前路径$ 

  1. 输入 要操作的命令 

  2. 程序负责执行相关的命令 

  3. Exit 程序的退出

程序的实现流程:

  1. 运行程序 ,打印提示符     ---- 提示符打印

linux@ubuntu:~$    

用户名@主机名:当前路径$ //getcwd -- sprintf();  //promt

  1. 输入 要操作的命令        ------ 解析命令 

cd .. 

ls 

touch 1.txt 

cat file 

cp src.txt dest.txt 

命令的格式:

<命令> [选项] [参数]

  1. 程序负责执行相关的命令 ---- 执行命令的功能 

  2. Exit 程序的退出 

[开 始]

     |

[打印提示符]

     |

[等待用户输入] //fgets

     |

[解析输入的命令]

     |

[根据命令执行对应的操作]

|

[结束]

所有命令按照其指定格式运行。

利用Linux中IO接收实现MiniShell

Minishell框图:

Minishell流程图:

代码分析

包含的头文件

MiniShell 程序首先包含了多个头文件,这些头文件提供了对文件描述符、目录遍历、系统调用等功能的支持。

 

#include <stdio.h> #include <fcntl.h> #include <dirent.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <pwd.h> #include <cstdlib> #include <ctime> #include <errno.h> #include <cstring> #include <limits.h>

辅助函数

程序定义了一个 location 函数,该函数根据给定的时间戳返回一个 tm 结构体指针,用于时间的本地化处理。

 

struct tm* location(const time_t *timer);

文件和目录列表显示

lsl 函数用于以 ls -l 的格式显示文件和目录的详细信息,包括类型、权限、链接数、所有者、组、大小、最后修改时间和文件名。

 

void lsl(const char *name) { // ... 省略部分代码 ... }

计算磁盘使用情况

total 函数计算当前目录下的总磁盘使用量,包括文件所占用的块数。

 

int total() { // ... 省略部分代码 ... }

全部文件和目录的显示

lslall 函数结合了 totallsl 函数,显示当前目录下所有文件和目录的详细信息,并显示总磁盘使用量。

 

void lslall() { // ... 省略部分代码 ... }

显示所有文件和目录(包括隐藏文件)

ls_a 函数显示当前目录下的所有文件和目录,包括以点(.)开头的隐藏文件。

 

void ls_a() { // ... 省略部分代码 ... }

搜索特定文件或目录

ls_f 函数搜索当前目录下与指定名称匹配的文件或目录,并显示其详细信息。

 

void ls_f(const char *filename) { // ... 省略部分代码 ... }

内容查看

cat 函数实现了内容查看功能,它打开指定的文件,并将其内容输出到标准输出。

 

int cat(const char* filename) { // ... 省略部分代码 ... }

当前目录显示

printdir 函数用于显示当前的工作目录,并在末尾添加 $ 提示符。

 

void printdir() { // ... 省略部分代码 ... }

目录切换

change_directory 函数接受一个目录名参数,并尝试切换到该目录。

 

void change_directory(const char *dir_name) { // ... 省略部分代码 ... }

文件复制

do_cp_filedo_cp_dir 函数分别用于复制文件和目录。它们处理文件的打开、读取、写入和关闭。

 

int do_cp_file(const char *src, const char *dest) { // ... 省略部分代码 ... } int do_cp_dir(const char *dir_s, const char *dir_d) { // ... 省略部分代码 ... }

创建空文件

touch 函数用于创建一个空文件,如果文件已存在,则不进行任何操作。

 

void touch(const char* filename) { // ... 省略部分代码 ... }

删除文件

delete_file 函数尝试删除指定的文件,并在失败时打印错误信息。

 

int delete_file(const char *filename) { // ... 省略部分代码 ... }

命令判断

judge 函数是 MiniShell 的核心,它读取用户输入的命令,并根据命令类型调用相应的函数。

 

int judge() { // ... 省略部分代码 ... }

主函数

调用命令判断函数并且打印相应内容

以下是我

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

struct tm*location(const time_t *timer);

void lsl(const char *name)
{
    struct stat st;
if (stat(name,&st) < 0)
    {
        perror("stat fail");
    }
    switch (st.st_mode&S_IFMT)
    {
    case S_IFSOCK:
        putchar('s');
        break;
    case S_IFLNK:
        putchar('l');
        break;

    case S_IFREG:  
        putchar('-');
        break;
    case S_IFBLK:  
        putchar('b');
        break;
    case S_IFDIR:  
        putchar('d');
        break;
    case S_IFCHR:  
        putchar('c');
        break;
    case S_IFIFO:  
        putchar('p');
        break;
    }

    st.st_mode&S_IRUSR?putchar('r'):putchar('-'); 
    st.st_mode&S_IWUSR?putchar('w'):putchar('-'); 
    st.st_mode&S_IXUSR?putchar('x'):putchar('-'); 
    
    st.st_mode&S_IRGRP?putchar('r'):putchar('-'); 
    st.st_mode&S_IWGRP?putchar('w'):putchar('-'); 
    st.st_mode&S_IXGRP?putchar('x'):putchar('-');

    st.st_mode&S_IROTH?putchar('r'):putchar('-'); 
    st.st_mode&S_IWOTH?putchar('w'):putchar('-'); 
    st.st_mode&S_IXOTH?putchar('x'):putchar('-');

    printf(" ");
    printf("%ld ",st.st_nlink);

    printf("%s ",getpwuid(st.st_uid)->pw_name);
    printf("%s ",getpwuid(st.st_gid)->pw_name);
    printf("%ld\t",st.st_size);

    struct tm* t = localtime(&st.st_mtime);
    printf("%2d月  %2d %02d:%02d ", t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min);
    printf(" %s\n",name);
}

int total()
{
    int t=0;
    char buf[100];
    getcwd(buf,sizeof(buf));
    DIR *dir = opendir(buf);
    struct dirent *pdir = NULL;
    while (pdir = readdir(dir))
    {
        struct stat st;
        if (stat(buf,&st) < 0)
        {
            perror("stat fail");
        }
    t=t+st.st_blocks/2;
    }
    return t+4;
}

void lslall()
{
    char buf[100];
    getcwd(buf,sizeof(buf));
    DIR *dir = opendir(buf);
    struct dirent *pdir = NULL;
    printf("total %d\n",total());
    while (pdir = readdir(dir))
    {
        if(pdir->d_name[0]!='.')
        lsl(pdir->d_name);
    }
}

void ls_a()
{
    char buf[100];
    getcwd(buf,sizeof(buf));
    DIR *dir = opendir(buf);
    struct dirent *pdir = NULL;
    while (pdir = readdir(dir))
    {
        printf("%s  ",pdir->d_name);
    }
    printf("\n");
}

void ls_()
{
    char buf[100];
    getcwd(buf,sizeof(buf));
    DIR *dir = opendir(buf);
    struct dirent *pdir = NULL;
    while (pdir = readdir(dir))
    {
        if(pdir->d_name[0]!='.')
        printf("%s  ",pdir->d_name);
    }
    printf("\n");
}

void ls_f(const char *filename)
{
    char buf[1000];
    getcwd(buf,sizeof(buf));
    DIR *dir = opendir(buf);
    struct dirent *pdir = NULL;
    while (pdir = readdir(dir))
    {
        if(strcmp(pdir->d_name,filename)==0&&pdir->d_type!=DT_DIR)
        {
            printf("%s  ",pdir->d_name);
            printf("\n");
        }
        if((strcmp(pdir->d_name,filename)==0)&&pdir->d_type==DT_DIR)
        {
            char buff[100];
            getcwd(buff,sizeof(buff));
            chdir(filename);
            ls_();
            chdir(buff);
            break;
        }
    }
}

int cat(const char* filename) {
    FILE *fp;
    char buf[1000];
    ssize_t bytes_read;
    fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("fopen");
        return -1;
    }
    while ((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0) {
        if (write(STDOUT_FILENO, buf, bytes_read) < 0) {
            perror("write");
            fclose(fp);
            return -1;
        }
    }
    if (bytes_read < 0) {
        perror("fread");
        fclose(fp);
        return -1;
    }
    fclose(fp);
    return 0;
}

void printdir()
{
    char buf[1000];
    fflush(stdout);
    getcwd(buf,sizeof(buf));
    //printf("%s$ ",buf);
    int ret=strlen(buf);
    buf[ret]='$';
    buf[ret+1]=' ';
    write(stdout->_fileno,buf,ret+2);
}

void change_directory(const char *dir_name) {
    // 获取当前工作目录
    char current_path[PATH_MAX];
    if (getcwd(current_path, sizeof(current_path)) == NULL) {
        perror("getcwd");
    }
    // 构建新的目录路径
    char new_path[PATH_MAX];
    if (dir_name[0] == '/') {
        // 如果传入的目录名是绝对路径,直接使用它
        snprintf(new_path, sizeof(new_path), "%s", dir_name);
    } else {
        // 如果传入的目录名是相对路径,将其与当前路径组合
        snprintf(new_path, sizeof(new_path), "%s/%s", current_path, dir_name);
    }

    // 切换到新的目录
    if (chdir(new_path) != 0) {
        perror("chdir");
    }
}

int do_cp_file(const char *src,const char *dest)
{
    int fd_s = open(src,O_RDONLY);
    int fd_d = open(dest,O_WRONLY|O_CREAT|O_TRUNC,0666);

    if (fd_s < 0 || fd_d < 0)
    {
        perror("open fail");
        return -1;
    }

    int ret = 0;
    char buf[100] = {0};

    while (ret = read(fd_s,buf,sizeof(buf)))
    {
        write(fd_d,buf,ret);
    }

    close(fd_s);
    close(fd_d);

    return 0;

}

void touch(const char* filename) 
{
    int fd=open(filename,O_RDWR|O_CREAT,0777);
    if(fd<0)
    {
        perror("open fail");
    }
}

int do_cp_dir(const char *dir_s,const char *dir_d)
{
    if (mkdir(dir_d,0777) < 0 && errno!=EEXIST)
    {
        perror("mkdir fail");
        return -1;
    }

    DIR *dir = opendir(dir_s);
    if (dir == NULL)
    {
        perror("opendir fail");
        return -1;
    }

    while (1)
    {
        struct dirent *pdir = readdir(dir);

        char spath[512];
        char dpath[512];

        if (pdir == NULL)
            break;
        //dir_s 
        //dir_s/
        if (pdir->d_name[0] != '.')
        {
            dir_s[strlen(dir_s)-1]=='/'?sprintf(spath,"%s%s",dir_s,pdir->d_name):sprintf(spath,"%s/%s",dir_s,pdir->d_name); //"dir_s/1.txt" 
            dir_d[strlen(dir_d)-1]=='/'?sprintf(dpath,"%s%s",dir_d,pdir->d_name):sprintf(dpath,"%s/%s",dir_d,pdir->d_name); //"dir_s/1.txt" 
            if (pdir->d_type == DT_DIR)
            {
                do_cp_dir(spath,dpath);
            }else 
            {
                do_cp_file(spath,dpath);

            }
        }
    }

    closedir(dir);
    return 0;
}

int delete_file(const char *filename) {
    if (remove(filename) != 0) {
        perror("remove");
        return -1; // 删除失败
    }
    return 0; // 删除成功
}

int judge()
{
    char buf[100];
    fflush(stdin);
    int ret = read(stdin->_fileno,buf,sizeof(buf));
    buf[ret-1]='\0';
    if(strcmp(buf,"ls -l")==0)
        lslall();
    if(strncmp(buf,"ls ",3)==0&&buf[3]!='-'&&buf[4]!='l')
    {
        char buff[100];
        char temp[100];
        if(buf[3]!='/')
        {
        char *arg = buf + 3;// 跳过 "ls -l " 前缀
        ls_f(arg);
        }
        else
        {
            printf("here");
            char *arg = buf + 3;
            getcwd(buff,sizeof(buff));
            change_directory(arg);
            getcwd(temp,sizeof(temp));
            ls_();
            change_directory(buff);
        }
    }
    
    if(strcmp(buf,"ls")==0)
        ls_();
    if(strcmp(buf,"ls -a")==0)
        ls_a();
    if(strncmp(buf,"ls -l ",6)==0)
    {
        char buff[100];
        char temp[100];
        char bufff[1000];
        getcwd(bufff,sizeof(bufff));
        DIR *dir = opendir(bufff);
        struct dirent *pdir = NULL;
        char *arg = buf + 6;
        int t=0;
        while(pdir=readdir(dir))
        {
        if(strcmp(pdir->d_name,arg)==0&&pdir->d_type!=DT_DIR)
        t=1;
        }
        if(t)
        {
            lsl(arg);
        }
        else
        {
            getcwd(buff,sizeof(buff));
            change_directory(arg);
            getcwd(temp,sizeof(temp));
            lslall(temp);
            change_directory(buff);
        }
    }
    if(strncmp(buf,"cat ",4)==0)
    {
        char *arg = buf + 4; // 跳过 "ls -l " 前缀
        cat(arg);
    }

    if(strncmp(buf,"cd ",3)==0)
    {
        char *arg = buf + 3; // 跳过 "ls -l " 前缀
        change_directory(arg);
    }
    if(strncmp(buf,"cp ",3)==0)
    {
        char *arg = buf + 3; // 跳过 "ls -l " 前缀
        char *source = strtok(arg, " "); // 使用空格分割字符串,获取源文件名
        char *destination = strtok(NULL, " "); // 继续分割,获取目标路径
        do_cp_dir(source,destination);
    }
    if(strncmp(buf,"rm ",3)==0)
    {
        char *arg = buf + 3; // 跳过 "ls -l " 前缀
        delete_file(arg);
    }
    if(strncmp(buf,"touch ",6)==0)
    {
        char *arg = buf + 6; // 跳过 "ls -l " 前缀
        touch(arg);
    }
    if(strncmp(buf,"exit",4)==0)
    {
        char *arg = buf + 4; // 跳过 "ls -l " 前缀
        return -1;
    }
    return 0;
}

int main(int argc, const char *argv[])
{
    while(1)
    {
        printdir();
        if(judge()==-1)
        break;
    }

    return 0;
}

MiniShell 是一个用 C 语言实现的轻量级 Shell 程序,它模拟了 UNIX Shell 的一些基本功能,如文件列表显示、内容查看、目录切换、文件复制、文件删除和创建空文件等。通过分析 MiniShell 的代码,我们可以深入了解 Shell 程序的工作原理,以及操作系统中文件和目录操作的底层实现。

MiniShell 的设计简洁,易于理解,适合作为学习材料,帮助初学者掌握系统编程和 Shell 脚本编写的基础知识。同时,MiniShell 也可以作为开发更高级 Shell 程序的基础框架,通过扩展和定制,实现更多复杂的功能。

总的来说,MiniShell 虽然功能有限,但它提供了一个很好的起点,让我们能够探索和学习 Shell 编程的核心概念。随着技术的不断进步和创新,我们可以期待 MiniShell 以及类似的工具在未来发挥更大的作用,帮助更多的人掌握计算机编程的精髓。

  • 40
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值