《操作系统真象还原》从零开始自制操作系统 自写源码实现 (shell相关文件)

本文档介绍了作者从零开始制作操作系统中,shell的相关文件实现,包括buildin_cmd.c中的路径处理函数、buildin_cmd.h的接口定义,以及shell.c中my_shell函数实现的内置命令如pwd、cd、ls等。详细展示了如何解析用户输入、处理绝对路径和工作目录切换。


专栏博客链接


《操作系统真象还原》从零开始自制操作系统 全章节博客链接


shell相关文件


编写完的buildin_cmd.c


#include "buildin_cmd.h"
#include "../fs/file.h"
#include "../fs/fs.h"
#include "debug.h"
#include "string.h"
#include "syscall.h"
#include "stdio-kernel.h"
#include "../fs/dir.h"
#include "../fs/fs.h"
#include "stdio.h"
#include "shell.h"

extern char final_path[160];

//在用户态就把路径给解析出来
void wash_path(char* old_abs_path,char* new_abs_path)
{
    ASSERT(old_abs_path[0] == '/');
    char name[MAX_FILE_NAME_LEN] = {0};
    char* sub_path = old_abs_path;
    sub_path = path_parse(sub_path,name);
    
    //如果直接就是根目录 直接返回即可
    if(name[0] == 0)
    {
        new_abs_path[0] = '/';
        new_abs_path[1] = 0;
        return;
    }
    new_abs_path[0] = 0; 
    strcat(new_abs_path,"/");
    while(name[0])
    {
        if(!strcmp("..",name))   //返回上级
        {
            char* slash_ptr = strrchr(new_abs_path,'/'); //等于移动到最偏右的/位置去
            if(slash_ptr != new_abs_path)   //如果为 /aaa 那么移动之后就到/的位置了 如果是/aaa/bbb 那么就会回到/aaa/
                *slash_ptr = 0; // 把/变成0
            else
	        *(slash_ptr+1) = 0; // 如果是 / 或者 /aaa 那么都回到/ 则把最右边+1置零位即可
        }
        else if(strcmp(".",name))  //如果不是到. 增加到后米纳即可 .等于没有作用 继续遍历即可
        {
            if(strcmp(new_abs_path,"/"))  //不是 / 防止出现// 的情况
                strcat(new_abs_path,"/");
            strcat(new_abs_path,name);
        }
        
        memset(name,0,MAX_FILE_NAME_LEN);
        if(sub_path)
            sub_path = path_parse(sub_path,name);
    }
}

//把path处理 . ..去掉 储存在final_path getcwd得到当前工作目录 + 相对路径 即绝对路径
void make_clear_abs_path(char* path,char* final_path)
{
    char abs_path[MAX_PATH_LEN] = {0};
    if(path[0] != '/')  //如果不是绝对路径就弄成绝对路径
    {
        memset(abs_path,0,MAX_PATH_LEN);
        if(getcwd(abs_path,MAX_PATH_LEN) != NULL)
        {
            if(!((abs_path[0] == '/') && abs_path[1] == 0))
                strcat(abs_path,"/");
        }
    }
    //把path 加到工作目录的头上
    strcat(abs_path,path);
    wash_path(abs_path,final_path);
}

// pwd命令中的内建函数 得到当前工作目录
void buildin_pwd(uint32_t argc,char** argv)
{
    //没有参数才可以
    if(argc != 1)
        printf("pwd: no argument support!\n");
    else
    {
        if(getcwd(final_path,MAX_PATH_LEN) != NULL)
            printf("%s\n",final_path);
        else
            printf("pwd: get current work directory failed\n");
    }   
}

// 支持一个参数 改变当前工作目录
char* buildin_cd(uint32_t argc,char** argv)
{
    if(argc > 2)
    {
        printf("cd: only support 1 argument!\n");
        return NULL;
    }
    if(argc == 1)
    {
        final_path[0] = '/';
        final_path[1] = 0;
    }
    else    make_clear_abs_path(argv[1],final_path);
    
    if(chdir(final_path) == -1)
    {
        printf("cd: no such directory %s\n",final_path);
        return NULL;
    }
    return final_path;   
}

// ls内建函数 仅支持-l -h -h等于不支持 哈哈
void buildin_ls(uint32_t argc,char** argv)
{
    char* pathname = NULL;
    struct stat file_stat;
    memset(&file_stat,0,sizeof(struct stat));
    bool long_info = false;
    uint32_t arg_path_nr = 0;  
    uint32_t arg_idx = 1;    //第一个字符串是 ls 跳过
    while(arg_idx < argc)    //仅仅支持 ls 或者 ls -l 或者 ls -l path的形式
    {
        if(argv[arg_idx][0] == '-')
        {
            if(!strcmp("-l",argv[arg_idx]))
                long_info = true;
            else if(!strcmp("-h",argv[arg_idx]))
            {
                printf("usage: -l list all all information about the file.\nnot support -h now sry - -\n");
                return;
            }
            else
            {
                printf("ls: invaild option %s\nTry 'ls -l' u can get what u want\n",argv[arg_idx]);
                return; 
            }
        }
        else
        {
            if(arg_path_nr == 0)
            {
                pathname = argv[arg_idx];
                arg_path_nr = 1;
            }
            else
            {
                printf("ls: only support one path\n");
                return;
            }
        }
        ++arg_idx;
    }
    
    if(pathname == NULL) //ls 或者 ls -l
    {
        //得到工作目录
        if(getcwd(final_path,MAX_PATH_LEN) != NULL)
	    pathname = final_path;
	else
	{
	    printf("ls: getcwd for default path failed\n");
	    return;
	}
    }
    else
    {
        make_clear_abs_path(pathname,final_path);
        pathname = final_path;
    }
    
    //目录下的文件
    if(stat(pathname,&file_stat) == -1)
    {
        printf("ls: cannot access %s: No such file or directory\n",pathname);
        return;
    }
    
    if(file_stat.st_filetype == FT_DIRECTORY)  //得到目录文件才继续
    {
        struct dir* dir = opendir(pathname); //得到目录指针
        struct dir_entry* dir_e = NULL;
        char sub_pathname[MAX_PATH_LEN] = {0};
        uint32_t pathname_len   = strlen(pathname);
        uint32_t last_char_idx  = pathname_len - 1;
        memcpy(sub_pathname,pathname,pathname_len);
        
        //方便后面得到当前目录下的文件stat信息 
        //加个/ 之后每个文件加文件名stat即可
        if(sub_pathname[last_char_idx] != '/') 
        {
            sub_pathname[pathname_len] = '/'; 
            ++pathname_len;
        }
        
        rewinddir(dir);  //目录指针指向0  方便readdir遍历目录项
        if(long_info)    // ls -l 这里是目录的ls
        {
            char ftype;
            printf("total: %d\n",file_stat.st_size);
            while((dir_e = readdir(dir)))    //通过readdir来遍历目录项 我还专门回去看了看这个函数
            {
                ftype = 'd';
                if(dir_e->f_type == FT_REGULAR)
                    ftype = '-';
                sub_pathname[pathname_len] = 0; //把字符串末尾设0 方便strcat函数
                strcat(sub_pathname,dir_e->filename);
                memset(&file_stat,0,sizeof(struct stat));
                if(stat(sub_pathname,&file_stat) == -1)
                {
                    printf("ls: cannot access %s:No such file or directory\n",dir_e->filename);
                    return;
                }
                printf("%c    %d    %d    %s\n",ftype,dir_e->i_no,file_stat.st_size,dir_e->filename);
            }
        }
        else  //仅仅是ls 把文件名写出来即可
        {
            while((dir_e = readdir(dir)))
                printf("%s  ",dir_e->filename);
            printf("\n");
        }
        closedir(dir);
    }
    else
    {
        if(long_info)
             printf("-    %d    %d    %s\n",file_stat.st_ino,file_stat.st_size,pathname);
	else
	    printf("%s\n",pathname);
    }
}

void buildin_ps(uint32_t argc,char** argv)
{
     //不应该有参数
    if(argc != 1)
    {
        printf("ps: no argument support!\n");
        return;
    }
    ps();
}

void buildin_clear(uint32_t argc,char** argv)
{
    if(argc != 1)
    {
        printf("clear: no argument support!\n");
        return;
    }
    clear();
}

int32_t buildin_mkdir(uint32_t argc,char** argv)
{
    //必须要有一个 安装路径参数
    int32_t ret = -1;
    if(argc != 2)
        printf("mkdir: only support 1 argument!\n");
    else
    {
        make_clear_abs_path(argv[1],final_path);
        if(strcmp("/",final_path))  //不是根目录 根目录一直都在
        {
            if(mkdir(final_path) == 0)
                ret = 0;
            else
                printf("mkdir: create directory %s failed.\n",argv[1]);
        }
    }
    return ret;
}

int32_t buildin_rmdir(uint32_t argc,char** argv)
{
    int32_t ret = -1;
    if(argc != 2)
        printf("rmdir: only support 1 argument!\n");
    else
    {
        make_clear_abs_path(argv[1],final_path);
        if(strcmp("/",final_path)) // 不能删除根目录
        {
            if(rmdir(final_path) == 0)
                ret = 0;
            else    printf("rmdir: remove %s failed\n");
        }
    }
    return ret;
}

int32_t buildin_rm(uint32_t argc,char** argv)
{
    int32_t ret = -1;
    if(argc != 2)
        printf("rm: only support 1 argument!\n");
    else
    {
        make_clear_abs_path(argv[1],final_path);
        if(strcmp("/",final_path))
        {
            if(unlink(final_path) == 0)
                ret = 0;
            else    printf("rm: delete %s failed\n",argv[1]); 
        }
    }
    return ret;
}

编写完的buildin_cmd.h


#ifndef __SHELL_BUILDIN_CMD_H
#define __SHELL_BUILDIN_CMD_H
#include "stdint.h"

void wash_path(char* old_abs_path,char* new_abs_path);
void make_clear_abs_path(char* path,char* final_path);
void buildin_pwd(uint32_t argc,char** argv);
char* buildin_cd(uint32_t argc,char** argv);
void buildin_ls(uint32_t argc,char** argv);
void buildin_ps(uint32_t argc,char** argv);
void buildin_clear(uint32_t argc,char** argv);
int32_t buildin_mkdir(uint32_t argc,char** argv);
int32_t buildin_rmdir(uint32_t argc,char** argv);
int32_t buildin_rm(uint32_t argc,char** argv);

#endif

编写完的shell.c


#include "shell.h"
#include "global.h"
#include "stdint.h"
#include "string.h"
#include "syscall.h"
#include "stdio.h"
#include "../fs/file.h"
#include "../kernel/debug.h"
#include "buildin_cmd.h"
#include "../fs/fs.h"
#include "../userprog/exec.h"

#define cmd_len    128 //最大支持128个字符
#define MAX_ARG_NR 16  //命令名外支持15个参数

char cmd_line[cmd_len] = {0};
char cwd_cache[64] = {0}; //目录的缓存 执行cd则移动到其他目录去
char final_path[MAX_PATH_LEN];
char* argv[MAX_ARG_NR];   //参数

//固定输出提示副
void print_prompt(void)
{
    printf("[Love 6@localhost %s]$ ",cwd_cache);
}

//最多读入count字节到buf
void readline(char* buf,int32_t count)
{
    ASSERT(buf != NULL && count > 0);
    char* pos = buf;
    //默认没有到回车就不停止 、一个一个字节读
    while(read(stdin_no,pos,1) != -1 && (pos - buf) < count)
    {
        switch(*pos)
        {
            //清屏
            case 'l'-'a':
                *pos = 0;
                clear();
                print_prompt();
                printf("%s",buf);  //把刚刚键入的字符打印出开
                break;
            
            //清除输入
            case 'u'-'a':
                while(buf != pos)
                {
                    putchar('\b');
                    *(pos--) = 0;
                }
                break;
                
            //和下面的回车一起
            case '\n':
            case '\r':
                *pos = 0;
                putchar('\n');
                return;
            
            case '\b':
                if(buf[0] != '\b') //阻止删除不是本次输出的信息
                {
                    --pos;
                    putchar('\b');
                }
                break;
            
            default:
                putchar(*pos);
                ++pos;
        }
    }
    printf("readline: cant fine entry_key in the cmd_line,max num of char is 128\n");
}

//解析键入的字符 token为分割符号
int32_t cmd_parse(char* cmd_str,char** argv,char token)
{
    ASSERT(cmd_str != NULL);
    int32_t arg_idx = 0;
    
    //初始化指针数组
    while(arg_idx < MAX_ARG_NR)
    {
        argv[arg_idx] = NULL;
        arg_idx++;
    }
    char* next = cmd_str;
    int32_t argc = 0;
    while(*next)
    {
        while(*next == token)	++next;
        if(*next == 0)	break;	//如果最后一个参数后面是空格 则之后就是结束符号了
        argv[argc] = next;     //无论怎么样 到这里都是有字符的地方 且argc表示目前存在第几个参数字符串
        
        while(*next && *next != token)  //要不结束了为0 或者到了分割符号
            ++next;
            
        if(*next)
            *(next++) = 0; //到最后就是设置字符串的末尾0 分割符号位置刚好处理为0 这样字符串有结束末尾了
                        //最后一个参数后面自然有'\0'
        if(argc > MAX_ARG_NR)
            return -1;
        
        ++argc;        
    }
    return argc;        //多少个参数
}

void my_shell(void)
{
    cwd_cache[0] = '/';
    cwd_cache[1] = 0;
    int argc = -1;
    while(1)
    {
        print_prompt();
        memset(cmd_line,0,cmd_len);
        memset(final_path,0,MAX_PATH_LEN);
        memset(argv,0,sizeof(char*) * MAX_ARG_NR);
        readline(cmd_line,cmd_len);
        if(cmd_line[0] == 0)
            continue;
            
        argc = -1;  
        argc = cmd_parse(cmd_line,argv,' ');
        if(argc == -1)
        {
            printf("num of arguments exceed %d\n",MAX_ARG_NR);
            continue;
        }
        if(!strcmp("ls",argv[0]))
            buildin_ls(argc,argv);
        else if(!strcmp("pwd",argv[0]))
            buildin_pwd(argc,argv);
        else if(!strcmp("ps",argv[0]))
            buildin_ps(argc,argv);
        else if(!strcmp("cd",argv[0]))
        {
            if(buildin_cd(argc,argv) != NULL)
            {
                memset(cwd_cache,0,MAX_PATH_LEN);
                strcpy(cwd_cache,final_path);
            }
        }
        else if(!strcmp("clear",argv[0]))
            buildin_clear(argc,argv);
        else if(!strcmp("mkdir",argv[0]))
            buildin_mkdir(argc,argv);
        else if(!strcmp("rmdir",argv[0]))
            buildin_rmdir(argc,argv);   
        else if(!strcmp("rm",argv[0]))
            buildin_rm(argc,argv); 
        else
        {
            printf("external command\n");
        }
    }
    PANIC("my_shell: should not be here");
}

编写完的shell.h


#ifndef __SHELL_SHELL_H
#define __SHELL_SHELL_H

#include "stdint.h"
#include "../fs/fs.h"

void print_prompt(void);
void readline(char* buf,int32_t count);
void my_shell(void);
int32_t cmd_parse(char* cmd_str,char** argv,char token);

#endif
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Love 6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值