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 以及类似的工具在未来发挥更大的作用,帮助更多的人掌握计算机编程的精髓。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/762943.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

印尼火出圈的本土网盟okspin助力slot游戏广告代投策略

印尼火出圈的本土网盟okspin助力slot游戏广告代投策略 在当今日益全球化的数字营销环境中&#xff0c;本土网盟广告平台在推广特定地区的产品和服务方面发挥着至关重要的作用。特别是在印尼这样的多元文化市场中&#xff0c;本土网盟okspin投放印尼slots游戏广告的优势尤为显著…

汽车零部件材料耐候性测试氙光太阳辐射系统试验箱

概述 汽车零部件等领域的材料耐候性测试是一项关键的质量控制环节&#xff0c;它关乎汽车部件在各种气候条件下的性能表现和寿命。塑料件光照老化实验箱&#xff0c;即氙灯老化试验箱&#xff0c;在其中扮演着至关重要的角色。通过模拟自然环境中的光照、温度、湿度等条件&…

顺序表(C语言详细版)

1. 线性表 线性表(lina list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串...... 线性表在逻辑上是线性结构&#xff0c;也就是说连续的一条直线。但是在物理结构上并…

开源205W桌面充电器,140W+65W升降压PD3.1快充模块(2C+1A口),IP6557+IP6538

开源一个基于IP6557和IP6538芯片的205W升降压快充模块&#xff08;140W65W&#xff09;&#xff0c;其中一路C口支持PD3.1协议&#xff0c;最高输出28V5A&#xff0c;另一路是A口C口&#xff0c;最高输出65W&#xff08;20V3.25A&#xff09;&#xff0c;可搭配一个24V10A的开关…

Ubuntu20.04 安装 cudatookit 12.2 + cudnn 安装

最简约的部署Ubuntu20.04深度学习环境的教程 1. 安装Ubuntu20.04 系统 B站详细的安装教程 简约安装版 2. 安装Nvidia显卡驱动 我参考了各种资料&#xff0c;重装系统&#xff0c;完美解决开机显示器黑屏无法进入桌面的情况 黑屏问题主要是由linux内核更新导致&#xff0c;…

混合注意力机制 -- Convolutional Block Attention Module(CBAM)

CBAM CBAM 模块概述 通道注意力模块&#xff08;Channel Attention Mechanism&#xff09;和空间注意力模块&#xff08;Spatial Attention Mechanism&#xff09;是注意力机制的两种主要形式&#xff0c;它们分别通过对通道维度和空间维度的特征图进行加权&#xff0c;从而使…

算法金 | Transformer,一个神奇的算法模型!!

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 抱个拳&#xff0c;送个礼 在现代自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Transformer 模型的出现带来了革命性的变…

每日一题-验证回文串

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” //验证回文串 #include<vector> class Solution { public:bool reverseString(char s) {return (s > a && s < z) ||(s > 0 && s < 9) ||(s…

Lesson 43 Hurry up!

Lesson 43 Hurry up! 词汇 of course 当然【口语】 经常出现在口语交际中&#xff1a; Of course not. 当然不。 同义词&#xff1a; Certainly 当然。 Certainly not. 当然不。 注意语气&#xff1a;略带挑衅。Sure. 当然。 Sure not. 当然不。 Not sure. 不一定。 kettle…

Pandas 学习笔记(一)

一、pandas简介 Pandas 是 Python 语言的一个扩展程序库&#xff0c;用于数据分析。 Pandas 名字衍生自术语 "panel data"&#xff08;面板数据&#xff09;和 "Python data analysis"&#xff08;Python 数据分析&#xff09;。 Pandas 是一个开放源码…

Python + OpenCV 酷游地址教学V鄋KWK3589

本篇文章汇整了一系列的Python OpenCV 教学&#xff0c;只要按照教学文的顺序阅读和实作&#xff0c;就可以轻松入门OpenCV&#xff0c;并透过OpenCV 实现许多影像相关的创意应用。 接下来我们来介绍OpenCV-- OpenCV 是一个跨平台的电脑视觉函式库( 模组) &#xff0c;可应用…

CesiumJS【Basic】- #042 绘制纹理线(Primitive方式)

文章目录 绘制纹理线(Primitive方式)1 目标2 代码2.1 main.ts3 资源文件绘制纹理线(Primitive方式) 1 目标 使用Primitive方式绘制纹理线 2 代码 2.1 main.ts var start = Cesium.Cartesian3

SSM泰华超市商品管理系统-计算机毕业设计源码11946

目 录 摘要 1 绪论 1.1 研究背景 1.2 研究意义 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据新增流程 3.2.2 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设…

一键把二次元老婆拉进现实(Stable Diffusion进阶:ControlNet LineArt模型)

大家好我是极客菌&#xff01;&#xff01;&#xff01; 操作&#xff0c;就能将二次元老婆拉进现实&#xff0c;成为你的专属女友。本文将带你深入了解ControlNet LineArt模型的使用方法&#xff0c;助你轻松实现这一梦想。 ControlNet LineArt模型是Stable Diffusion的最新…

AI大模型日报#0701:Meta发布LLM Compiler、扒一扒Sora两带头人博士论文

导读&#xff1a;AI大模型日报&#xff0c;爬虫LLM自动生成&#xff0c;一文览尽每日AI大模型要点资讯&#xff01;目前采用“文心一言”&#xff08;ERNIE-4.0-8K-latest&#xff09;生成了今日要点以及每条资讯的摘要。欢迎阅读&#xff01;《AI大模型日报》今日要点&#xf…

32.哀家要长脑子了!

1.299. 猜数字游戏 - 力扣&#xff08;LeetCode&#xff09; 公牛还是挺好数的&#xff0c;奶牛。。。妈呀&#xff0c;一朝打回解放前 抓本质抓本质&#xff0c;有多少位非公牛数可以通过重新排列转换公牛数字&#xff0c;意思就是&#xff0c;当这个数不是公牛数字时&#x…

控制器方法执行流程和 @InitBinder【Spring源码学习】

控制器方法执行流程 InitBinder 加在ControllerAdvice中 首先说明ControllerAdvice和aop没有任何关系&#xff01; 加在ControllerAdvice中只对所有控制器都生效 全局的在开始时就会保存到handlerMappingAdapter中的cache中&#xff1b; 加在Controller中 加在controller中只对…

TS---typescript的安装和tsc命令使用

什么是TS---typescript&#xff1f; &#xff08;TypeScript是Microsoft公司注册商标&#xff09; TypeScript具有类型系统&#xff0c;且是JavaScript的超集&#xff0c; 它可以编译成普通的JavaScript代码。TypeScript支持任意浏览器&#xff0c;任意环境&#xff0c;任意系…

仓库管理系统24--统计报表

原创不易&#xff0c;打字不易&#xff0c;截图不易&#xff0c;多多点赞&#xff0c;送人玫瑰&#xff0c;留有余香&#xff0c;财务自由明日实现 1、引用LiveCharts 2、创建LiveChartViewModel using GalaSoft.MvvmLight; using LiveCharts.Wpf; using LiveCharts; using Sy…

手把手搞定报名亚马逊科技认证

引言 亚马逊云科技认证考试为我们这些技术从业者提供了提升专业技能的机会。无论选择线上还是线下考试&#xff0c;每种方式都有其独特的优势和挑战。选择合适的考试方式将帮助我们更好地展示自己的技术水平。以下是我对不同考试方式的优缺点介绍&#xff0c;以及各科目的考试…