利用Llinux中的IO接口实现MiniShenll

文章介绍了一个名为Minishell的项目,它实现了基本的Linux命令,如ls、touch、rm等,以及文件和目录的操作。用户可以与终端交互,执行各种文件系统操作。此外,Minishell还有记录终端输入的日志功能,将命令和时间保存在work.txt文件中。代码示例展示了如何处理不同命令,如ls命令的不同选项,以及如何处理文件和目录的创建、删除等操作。
摘要由CSDN通过智能技术生成
  • Main.c: 调用Command.c中的函数接口

  • Terminal.c: 用于实现从终端的接收命令

  • Command.c: 用于实现将传递进来的命令正确无误的操作进行,如ls,touch,cd, cp等等基本命令

  • Terminal.h和Command.h: 存放模块头文件

——————————————————————————————————————————

minishell功能

ls -> 查询当前目录文件

ls -a -> 查询当前目录文件包括隐藏文件

ls -l -> 查询当前目录文件详情

ls -l -name -> 查询指定路径的文件详情

touch -> 新建文件

rm -> 删除文件

mkdir -> 新建文件夹

rmdir -> 删除文件夹

cd -> 进入目标文件夹

cp -> 拷贝文件

mv -> 移动/重命名文件

pwd -> 在终端显示当前路径

cat -> 在终端显示文件类型

ln -> 创建文件硬链接

help -> 获取命令使用帮助

***具有终端输入日记记录功能,work.txt文件实时记录终端情况***

——————————————————————————————————————————

代码展示:


head.h

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

main.c

#include "head.h"
#include "command.h"

int main(int argc, char const *argv[])
{
    printf("输入exit后推出minishell\n");
    printf("输入help获取命令提示\n");
    commands(".");
    return 0;
}

Command.h

#ifndef __commands_c__
#define __commands_c__

extern void commands(char *p);
extern void terminal(char *pcomd,char **ppstr,char *pname);
#endif

Command.c

#include "head.h"
#include "terminal.h"

void LS(char *pc)
{
    DIR *dp = NULL;
    struct dirent *pp = NULL;

    dp = opendir(pc);
    if (NULL == dp)
    {
        perror("LS- opendir failed\n");
        return;
    }

    while (1)
    {
        pp = readdir(dp);
        if (NULL == pp)
        {
            break;
        }
        if ('.' == pp->d_name[0])
        {
            continue;
        }
        printf("%s  ", pp->d_name);
    }
    printf("\n");
    closedir(dp);
    return;
}

void LSa(char *pc)
{
    DIR *dp = NULL;
    struct dirent *pp = NULL;

    dp = opendir(pc);
    if (NULL == dp)
    {
        perror("LS-a- opendir failed\n");
        return;
    }

    while (1)
    {
        pp = readdir(dp);
        if (NULL == pp)
        {
            break;
        }
        printf("%s  ", pp->d_name);
    }
    closedir(dp);
    printf("\n");
    return;
}

void LSl(char *pc)
{
    DIR *dp = NULL;
    struct dirent *pp = NULL;
    struct stat buf;
    struct passwd *username = NULL;
    struct group *gpname = NULL;
    struct tm *ptm = NULL;
    int ret = 0;

    dp = opendir(pc);
    if (NULL == dp)
    {
        perror("fail to opendir\n");
        return;
    }
    while (1)
    {
        pp = readdir(dp);              // 接受返回一个的dirent结构体的指针;
        ret = lstat(pp->d_name, &buf); // lstat:从文件名读取文件信息到&buf指向的结构体空间中,失败返回-1,成功返回0;

        if (NULL == pp)
        {
            break;
        }
        if ('.' == pp->d_name[0])
        {
            continue;
        }

        if (-1 == ret)
        {
            perror("fail to stat\n");
            return;
        }
        // 文件类型判断:
        if (S_ISREG(buf.st_mode))
        {
            printf("-");
        }
        else if (S_ISLNK(buf.st_mode))
        {
            printf("l");
        }
        else if (S_ISBLK(buf.st_mode))
        {
            printf("b");
        }
        else if (S_ISSOCK(buf.st_mode))
        {
            printf("s");
        }
        else if (S_ISDIR(buf.st_mode))
        {
            printf("d");
        }
        else if (S_ISCHR(buf.st_mode))
        {
            printf("c");
        }
        else if (S_ISFIFO(buf.st_mode))
        {
            printf("p");
        }
        // 文件权限判断:
        buf.st_mode &S_IRUSR ? putchar('r') : putchar('-');
        buf.st_mode &S_IWUSR ? putchar('w') : putchar('-');
        buf.st_mode &S_IXUSR ? putchar('x') : putchar('-');
        buf.st_mode &S_IRGRP ? putchar('r') : putchar('-');
        buf.st_mode &S_IWGRP ? putchar('w') : putchar('-');
        buf.st_mode &S_IXGRP ? putchar('x') : putchar('-');
        buf.st_mode &S_IROTH ? putchar('r') : putchar('-');
        buf.st_mode &S_IWOTH ? putchar('w') : putchar('-');
        buf.st_mode &S_IXOTH ? putchar('x') : putchar('-');
        printf(" ");
        // 硬链接数目:
        printf("%ld ", buf.st_nlink);
        // 用户名 组长名:
        username = getpwuid(buf.st_uid);
        gpname = getgrgid(buf.st_gid);
        printf("%s ", username->pw_name);
        printf("%s ", gpname->gr_name);
        // 字节数
        printf("%6ld ", buf.st_size);
        // 时间
        ptm = localtime(&buf.st_ctime);
        printf("%d月  %2d %2d:%2d ", ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min);
        // 文件名
        printf("%s ", pp->d_name);
        printf("\n");
    }
    closedir(dp);

    return;
}

void LSn(char *pn)
{
    DIR *dp = NULL;
    struct dirent *pp = NULL;
    FILE *fp = NULL;
    int ret = 0;

    dp = opendir(pn);
    if (NULL == dp)
    {
        fp = fopen(pn,"r");
        if (NULL == fp)
        {
            printf("cannot access '%s':No such file or directory\n",pn);
            //perror("");
            return;
        }
        else
        {
            printf("%s\n", pn);
            fclose(fp);
            return;            
        }

    }

    while (1)
    {
        pp = readdir(dp);
        if (NULL == pp)
        {
            break;
        }
        if ('.' == pp->d_name[0])
        {
            continue;
        }
        printf("%s  ", pp->d_name);
    }
    printf("\n");
    return;

    closedir(dp);
}

void LSln(char *pn)
{
    DIR *dp = NULL;
    struct dirent *pp = NULL;
    struct stat buf;
    struct passwd *username = NULL;
    struct group *gpname = NULL;
    struct tm *ptm = NULL;
    int ret = 0;

    dp = opendir(pn);
    if (NULL == dp)     //------------------打开失败则返回pn的文件信息
    {

        // pp = readdir(dp);              // 接受返回一个的dirent结构体的指针;

        ret = lstat(pn, &buf); // lstat:从文件名读取文件信息到&buf指向的结构体空间中,失败返回-1,成功返回0;

        if (-1 == ret)
        {
            printf("ls -l '%s' fail to stat: No such file or directory\n",pn);
            return;
        }

        // 文件类型判断:
        if (S_ISREG(buf.st_mode))
        {
            printf("-");
        }
        else if (S_ISLNK(buf.st_mode))
        {
            printf("l");
        }
        else if (S_ISBLK(buf.st_mode))
        {
            printf("b");
        }
        else if (S_ISSOCK(buf.st_mode))
        {
            printf("s");
        }
        else if (S_ISDIR(buf.st_mode))
        {
            printf("d");
        }
        else if (S_ISCHR(buf.st_mode))
        {
            printf("c");
        }
        else if (S_ISFIFO(buf.st_mode))
        {
            printf("p");
        }

        // 文件权限判断:
        buf.st_mode &S_IRUSR ? putchar('r') : putchar('-');
        buf.st_mode &S_IWUSR ? putchar('w') : putchar('-');
        buf.st_mode &S_IXUSR ? putchar('x') : putchar('-');
        buf.st_mode &S_IRGRP ? putchar('r') : putchar('-');
        buf.st_mode &S_IWGRP ? putchar('w') : putchar('-');
        buf.st_mode &S_IXGRP ? putchar('x') : putchar('-');
        buf.st_mode &S_IROTH ? putchar('r') : putchar('-');
        buf.st_mode &S_IWOTH ? putchar('w') : putchar('-');
        buf.st_mode &S_IXOTH ? putchar('x') : putchar('-');

        printf(" ");
        // 硬链接数目:
        printf("%ld ", buf.st_nlink);
        // 用户名 组长名:
        username = getpwuid(buf.st_uid);
        gpname = getgrgid(buf.st_gid);
        printf("%s ", username->pw_name);
        printf("%s ", gpname->gr_name);
        // 字节数

        printf("%6ld ", buf.st_size);
        // 时间
        ptm = localtime(&buf.st_ctime);
        printf("%d月  %2d %2d:%2d ", ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min);

        // 文件名
        printf("%s ", pn);
        printf("\n");

        return;
    }
    while (1)
    {
        pp = readdir(dp);      // 接受返回一个的dirent结构体的指针;
        ret = lstat(pn, &buf); // lstat:从文件名读取文件信息到&buf指向的结构体空间中,失败返回-1,成功返回0;

        if (NULL == pp)
        {
            break;
        }
        if ('.' == pp->d_name[0])
        {
            continue;
        }

        if (-1 == ret)
        {
            perror("LS-l-n- fail to stat\n");
            return;
        }
        // 文件类型判断:
        if (S_ISREG(buf.st_mode))
        {
            printf("-");
        }
        else if (S_ISLNK(buf.st_mode))
        {
            printf("l");
        }
        else if (S_ISBLK(buf.st_mode))
        {
            printf("b");
        }
        else if (S_ISSOCK(buf.st_mode))
        {
            printf("s");
        }
        else if (S_ISDIR(buf.st_mode))
        {
            printf("d");
        }
        else if (S_ISCHR(buf.st_mode))
        {
            printf("c");
        }
        else if (S_ISFIFO(buf.st_mode))
        {
            printf("p");
        }
        // 文件权限判断:
        buf.st_mode &S_IRUSR ? putchar('r') : putchar('-');
        buf.st_mode &S_IWUSR ? putchar('w') : putchar('-');
        buf.st_mode &S_IXUSR ? putchar('x') : putchar('-');
        buf.st_mode &S_IRGRP ? putchar('r') : putchar('-');
        buf.st_mode &S_IWGRP ? putchar('w') : putchar('-');
        buf.st_mode &S_IXGRP ? putchar('x') : putchar('-');
        buf.st_mode &S_IROTH ? putchar('r') : putchar('-');
        buf.st_mode &S_IWOTH ? putchar('w') : putchar('-');
        buf.st_mode &S_IXOTH ? putchar('x') : putchar('-');
        printf(" ");
        // 硬链接数目:
        printf("%ld ", buf.st_nlink);
        // 用户名 组长名:
        username = getpwuid(buf.st_uid);
        gpname = getgrgid(buf.st_gid);
        printf("%s ", username->pw_name);
        printf("%s ", gpname->gr_name);
        // 字节数
        printf("%6ld ", buf.st_size);
        // 时间
        ptm = localtime(&buf.st_ctime);
        printf("%d月  %2d %2d:%2d ", ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min);
        // 文件名
        printf("%s ", pp->d_name);
        printf("\n");
    }
    closedir(dp);

    return;
}
//--------------touch可实现创建文件pn,如果存在则不做改变
void TOUCH(char *pn)
{
    FILE *fp = NULL;

    fp = fopen(pn, "a");
    if (NULL == fp)
    {
        perror("Touch- fopen failed");
        return;
    }
    fclose(fp);
    return;
}
//--------------rm可实现删除文件pn
void RM(char *pn)
{
    FILE *fp = NULL;
    fp = fopen(pn,"r");
    if(NULL == fp)
    {
        printf("rm: cannot remove '%s': No such file or directory\n",pn);
        return;
    }
    else
    {
        remove(pn);
        fclose(fp);      
    }

    return;
}
//--------------mkdir可实现创建指定路径的文件夹
void MKDIR(char *pn)
{
    mkdir(pn, 0777);
    return;
}
//--------------rmdir可实现删除指定路径的文件夹
void RMDIR(char *pn)
{
    int ret = 0;

    ret = rmdir(pn);
    if (-1 == ret)
    {
        printf("RMDIR:failed to REMOVE '%s':Directory not empty\n", pn);
    }
    return;
}
//--------------cd可实现在转到指定路径
void CD(char *pn)
{
    int ret = 0;

    ret = chdir(pn);
    if (-1 == ret)
    {
        perror("failed chdir\n");
        return;
    }
    return;
}
//--------------cp可实现在pn1为普通文件的情况下对其进行拷贝
void CP(char *pn1, char *pn2)
{
    struct stat buf1;
    struct stat buf2;
    char tmp[4096] = {0};
    int ret1 = 0;
    int ret2 = 0;
    FILE *fp1 = NULL;
    FILE *fp2 = NULL;
    DIR *dp2 = NULL;
    DIR *dp1 = NULL;
//===============================================================================
// 获取当前的绝对路径
    char str[4096] = {0};
    getcwd(str, sizeof(str));
//===============================================================================
    dp1 = opendir(pn1); //-----------用目录型去打开pn1,如果打开失败则用文件型去打开pn1
    if (dp1 == NULL)
    {
        fp1 = fopen(pn1, "r");
        if (NULL == fp1)
        {
            printf("CP-1- NOT FIND %s\n", pn1); //------------若pn1不存在返回NOT FIND
            return;
        }
        ret1 = lstat(pn1, &buf1);
        if (-1 == ret1)
        {
            perror("CP-1- fail to stat\n");
            return;
        }
    }
//===============================================================================
    dp2 = opendir(pn2); //-----------用目录型去打开pn2,如果打开失败则用文件型去打开pn2
    if (dp2 == NULL)
    {
        fp2 = fopen(pn2, "w");
        if (NULL == fp2)
        {
            perror("CP-fp2- open failed\n");
        }
        ret2 = lstat(pn2, &buf2);
        if (-1 == ret2)
        {
            perror("CP-2- fail to stat\n");
            return;
        }
        if (dp1 == NULL)
        {
            while (1) //--------------------------复制pn1到pn2的目录下并更名为pn2
            {
                ret1 = fread(tmp, 1, sizeof(tmp), fp1);
                if (0 == ret1)
                {
                    break;
                }
                fwrite(tmp, 1, ret1, fp2);
            }
                fclose(fp1);
                fclose(fp2);
        }
    }
    else //-----------------------------------------当pn2为路径时,复制pn1到pn2路径下
    {
        chdir(pn2);
        fp2 = fopen(pn1, "w");

        while (1)
        {
            int ret = fread(tmp, 1, sizeof(tmp), fp1);
            if (0 == ret)
            {
                break;
            }
            fwrite(tmp, 1, ret, fp2);
        }
        fclose(fp1);
        fclose(fp2);
        chdir(str);
        closedir(dp2);
    }
//===========================================================
    return;
}
//--------------pwd可获取绝对路径
void PWD(char *pn)
{
    char str[4096] = {0};
    getcwd(str, sizeof(str));
    printf("%s\n", str);

    return;
}
//--------------cat可将pn的内容展示到终端
void CAT(char *pn)
{
    char str[2048] = {0};
    char *pstr = NULL;
    FILE *fp = NULL;


    fp = fopen(pn, "r");
    if (NULL == fp)
    {
        perror("fopen failed\n");
        return;
    }

    while (1)
    {
        pstr = fgets(str, sizeof(str), fp);
        if (NULL == pstr)
        {
            break;
        }
        fputs(str, stdout);
    }
    fclose(fp);
    return;
}
//--------------ln可创造一个硬链接
void LN(char *pn1, char *pn2)
{
    FILE *fp = NULL;

    fp = fopen (pn2,"r");
    if (NULL == fp)
    {
        link(pn1, pn2);
        return;
    }
    else
    {
        printf("ln: failed to create hard link '%s': File exists\n",pn2);
    }

    return;
}
//---------------该函数将当前输入的命令和时间存入到worktime.txt
void WORKTIME(char *pnc, char *pname)
{
    FILE *fp = NULL;
    struct stat buf;
    struct tm *ctime = NULL;
    int ret = 0;
    int times = 0;
    char gctime[256] = {0};
    char str[4096] = {0};
    getcwd(str, sizeof(str));

    chdir("/home/linux/dn/xiangmu");

    fp = fopen("worktime.txt", "a");

    ret = lstat(str, &buf);
    if (-1 == ret)
    {
        perror("fail to stat\n");
        chdir(str);
        return;
    }
    //==================================================================================
    //----获取命令并将其传入worktime.txt
    ctime = localtime(&buf.st_ctime);
    char ntime[256] = {0};
    strftime(ntime, sizeof(ntime), "%Y-%m-%d %H:%M:%S", ctime);
    fwrite(ntime, strlen(ntime), 1, fp);
    fputs(" : ", fp);
    fwrite(pname, strlen(pname), 1, fp);

    fputs("\n", fp);
    fclose(fp);

    chdir(str);
    return;
}

void commands(char *p)
{   
    while (1)
    {

        int i = 0;

        char nc[4096] = {0};
        char str[4096] = {0};
        char hname[32] = {0};
        char name[256] = {0};
        char *pstr[64] = {0};

        struct passwd *username = NULL;
        struct stat buf;

        lstat(p, &buf);
        gethostname(hname, sizeof(hname));

        username = getpwuid(buf.st_uid);

        getcwd(str, sizeof(str));
        printf("%s@%s:%s¥ ", username->pw_name, hname, str); // 显示绝对路径 ----此处应为相对路径

        terminal(nc, pstr, name);

        WORKTIME(nc, name);
        if(NULL == pstr[0])
        {
            printf("请输入命令:\n");
            continue;
        }   
        if (!strcmp(pstr[0], "exit"))
        {
            break;
        }

        else if (!strcmp(pstr[0], "ls"))
        {
            if (NULL == pstr[1])
            {
                LS(p);
            }
            else if (!strcmp(pstr[1], "-a"))
            {
                LSa(p);
            }
            else if (!strcmp(pstr[1], "-l"))
            {
                if (NULL == pstr[2])
                {
                    LSl(p);
                }
                else
                {
                    LSln(pstr[2]);
                }
            }
            else
            {
                LSn(pstr[1]);
            }
        }

        else if (!strcmp(pstr[0], "touch"))
        {
            TOUCH(pstr[1]);
        }

        else if (!strcmp(pstr[0], "rm"))
        {
            RM(pstr[1]);
        }

        else if (!strcmp(pstr[0], "mkdir"))
        {
            MKDIR(pstr[1]);
        }

        else if (!strcmp(pstr[0], "rmdir"))
        {
            RMDIR(pstr[1]);
        }

        else if (!strcmp(pstr[0], "cd"))
        {
            CD(pstr[1]);
        }

        else if (!strcmp(pstr[0], "cp"))
        {
            CP(pstr[1], pstr[2]);
        }

        else if (!strcmp(pstr[0], "pwd"))
        {
            PWD(p);
        }

        else if (!strcmp(pstr[0], "cat"))
        {
            CAT(pstr[1]);
        }

        else if (!strcmp(pstr[0], "ln"))
        {
            LN(pstr[1], pstr[2]);
        }
        
        else if (!strcmp(pstr[0],"help"))
        {
            CAT("help.txt");
        }
        else
            {
                printf("输入命令错误!\n");
                continue;
            }
    }
    return;
}

terminal.h----------------------------------终端交互模块头文件

#ifndef __terminals_c__
#define __terminals_c__

extern void terminal(char *pcomd,char **ppstr,char *pname);

#endif

terminal.c-----------------------------------终端交互模块

#include "head.h"

void terminal(char *pcomd,char **ppstr,char *pname)
{
    int i = 0;
    int len = 0;

    fgets(pcomd,4096,stdin);   //由于此处从输入流接收到额外的'\n'

    len = strlen(pcomd);            
    pcomd[len-1] = '\0';        //所以在此处将'\n'改为'\0'

    strcpy(pname,pcomd);

    ppstr[0] = strtok(pcomd," "); 

//========================================================================
//对接收到的数组以‘ ’为标识进行分割
    i = 1;
    while(1)
    {
        ppstr[i] = strtok(NULL," ");
        if(NULL == ppstr[i])
        {
            break;
        }    
        i++;
    }
//========================================================================
    return;
}

makefile

OBJ:=minishell.out
OBJS+=main.c
OBJS+=command.c
OBJS+=terminal.c

$(OBJ):$(OBJS)
    gcc $(OBJS) -o $(OBJ)

.PHONY:
clean:
    rm $(OBJ)
install:
    sudo cp $(OBJ) /usr/bin
uninstall:
    sudo rm /usr/bin/$(OBJ)
memcheck:
    valgrind --tool=memcheck --leak-check=full ./$(OBJ)

help-----------------------------------命令提示函数

ls                          在终端显示出当前目录下的所有文件
ls -a                       在终端显示出当前目录下的所有文件包括隐藏文件
ls -ln                      在终端显示出当前目录下的所有文件的详细信息
ls filename                 在终端显示filename
ls -l filename              在终端显示filename的详细信息
touch filename              创建一个filename文件
rm filename                 删除filename文件
mkdir filename              创建一个filename文件夹
rmdir filename              删除filename文件夹(文件夹需为空)
cd pathname                 改变路径为该路径名
cp filename parameter       复制filename文件到参数2下
pwd                         在终端显示绝对路径
cat filename                在终端显示filename的内容
ln filename1 filename2      创建一个硬链接,由filename2指向filename1


运行Minishell后显示如下


写在最后:

由于是我第一次创作,很多代码使用不到位,也有冗杂的程序步骤,在我测试过程中存在的bug已经改正,如果有幸被使用,希望得到诸君的指点^_^

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值