阶段练习——minishell

目录

(一)文件复制(my_cp函数)

(二)文件内容查看(my_cat函数) 

 (三)切换目录(my_cd函数)

(四)列出目录内容(my_ls函数)

 (五)详细列出目录内容(my_ll函数)

(六)创建符号链接(my_symlink函数)

(七)创建硬链接(my_link函数)

(八)删除文件(my_rm函数)

(九)创建空文件(my_touch函数)

(十)移动文件(my_mv函数)

(十一)主函数(main函数)


一、项目概述
        本项目是一个使用 C 语言编写的程序,其目的是模拟常见的文件操作命令,为用户提供类似于在操作系统中执行基本文件操作的功能。这些功能包括文件的复制、内容查看、目录切换、目录内容的列出(包括简单列出和详细列出)、创建符号链接和硬链接、文件的删除、创建空文件以及文件的移动等。

二、功能模块设计

(一)文件复制(my_cp函数)

将指定的源文件内容逐行复制到目标文件中

int my_cp(const char *src_file, const char *dst_file)
{// 以只读模式打开源文件FILE * src_fp = fopen(src_file, "r");// 以写入模式打开目标文件,如果目标文件不存在则创建FILE * dst_fp = fopen(dst_file, "w");// 如果源文件或目标文件打开失败if (NULL == src_fp || NULL == dst_fp){// 打印错误信息perror("fopen");// 返回 1 表示操作失败return 1;}// 定义一个 1024 字节大小的缓冲区char buf[1024] = { 0 };// 进入一个无限循环while (1){// 从源文件中读取一行内容到缓冲区char* s = fgets(buf, sizeof(buf), src_fp);// 如果读取失败(到达文件末尾)if (NULL == s){// 退出循环break;}// 将缓冲区中的内容写入到目标文件fputs(buf, dst_fp);}// 关闭目标文件fclose(dst_fp);// 关闭源文件fclose(src_fp);// 返回 0 表示操作成功return 0;
}

(二)文件内容查看(my_cat函数) 

打开指定的文件,并将其内容逐行打印在终端上

int my_cat(const char *src_file)
{// 以只读模式打开指定文件FILE * fp = fopen(src_file, "r");// 如果文件打开失败if (fp == NULL){// 打印错误信息perror("fopen");}// 定义一个 1024 字节大小的缓冲区char buf[1024] = { 0 };// 进入一个无限循环while (1){// 从文件中读取一行内容到缓冲区char *s = fgets(buf, sizeof(buf), fp);// 如果读取失败(到达文件末尾)if (s == NULL){// 退出循环break;}// 在控制台打印缓冲区中的内容printf("%s", buf);}// 关闭文件fclose(fp);
}

 (三)切换目录(my_cd函数)

实现切换到指定目录,并获取切换后的当前工作目录

void my_cd(const char * src_file)
{// 定义一个 125 字节大小的缓冲区char buf[125] = { 0 };// 切换到指定的目录chdir(src_file);// 获取当前工作目录,并将其存储在缓冲区中getcwd(buf, sizeof(buf));
}

(四)列出目录内容(my_ls函数)

打开指定的目录,并在终端打印出目录中的文件名

void my_ls(const char *src_file)
{// 打开指定的目录DIR * dir = opendir(src_file);// 如果目录打开失败if (dir == NULL){// 打印错误信息perror("opendir");}// 定义一个 125 字节大小的缓冲区char buf[125] = { 0 };// 进入一个无限循环while (1){// 读取目录中的一个文件或子目录信息struct dirent *info = readdir(dir);// 如果读取失败(到达目录末尾)if (info == NULL){// 退出循环break;}// 在控制台打印文件或子目录的名称printf("%s  ", info->d_name);}// 在控制台打印一个换行符printf("\n");// 关闭目录closedir(dir);
}

 (五)详细列出目录内容(my_ll函数)

        打开指定目录,并详细地打印出目录中每个文件的各种信息,包括文件类型、权限、所有者、所属组、大小、修改时间和文件名等

void my_ll(const char *src_file)
{// 打开指定的目录DIR * dir = opendir(src_file);// 如果目录打开失败if (dir == NULL){// 打印错误信息perror("opendir");}// 进入一个无限循环while (1){// 读取目录中的一个文件或子目录信息struct dirent * info = readdir(dir);// 如果读取失败(到达目录末尾)if (info == NULL){// 退出循环break;}// 用于存储文件的完整路径struct stat st;// 将文件路径拼接完整char fullPath[512];snprintf(fullPath, sizeof(fullPath), "%s/%s", src_file, info->d_name);// 获取文件的状态信息int ret = stat(fullPath, &st);// 如果获取状态信息失败if (ret == -1){// 打印错误信息perror("stat");}// 根据文件类型进行标记输出if (S_ISREG(st.st_mode)){fputc('-', stdout);}else if (S_ISDIR(st.st_mode)){fputc('d', stdout);}else if (S_ISCHR(st.st_mode)){fputc('c', stdout);}else if (S_ISBLK(st.st_mode)){fputc('b', stdout);}else if (S_ISFIFO(st.st_mode)){fputc('f', stdout);}else if (S_ISLNK(st.st_mode)){fputc('l', stdout);}else if (S_ISSOCK(st.st_mode)){fputc('o', stdout);}// 根据用户权限进行标记输出if (st.st_mode & S_IRUSR){fputc('r', stdout);}else{fputc('-', stdout);}if (st.st_mode & S_IWUSR){fputc('w', stdout);}else{fputc('-', stdout);}if (st.st_mode & S_IXUSR){fputc('x', stdout);}else{fputc('-', stdout);}// 根据组权限进行标记输出if (st.st_mode & S_IRGRP){fputc('r', stdout);}else{fputc('-', stdout);}if (st.st_mode & S_IWGRP){fputc('w', stdout);}else{fputc('-', stdout);}if (st.st_mode & S_IXGRP){fputc('x', stdout);}else{fputc('-', stdout);}// 根据其他用户权限进行标记输出if (st.st_mode & S_IROTH){fputc('r', stdout);}else{fputc('-', stdout);}if (st.st_mode & S_IWOTH){fputc('w', stdout);}else{fputc('-', stdout);}if (st.st_mode & S_IXOTH){fputc('x', stdout);}else{fputc('-', stdout);}// 获取文件的修改时间,并转换为本地时间格式struct tm *t = localtime(&st.st_mtime);// 获取文件所有者的用户信息uid_t uid = st.st_uid;struct passwd * pw = getpwuid(uid);// 如果获取用户信息失败if (pw == NULL){// 打印错误信息perror("getpwuid");}// 获取文件所属组的信息gid_t gid = st.st_gid;struct group * gr = getgrgid(gid);// 如果获取组信息失败if (gr == NULL){// 打印错误信息perror("getgrgid");}// 打印文件的链接数、所有者用户名、所属组名、文件大小、修改月份、日期、时间和文件名printf(" %2lu %-5s %-5s %5lu %02d 月  %d %d:%2d %s\n",st.st_nlink, pw->pw_name, gr->gr_name, st.st_size,t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, info->d_name);}// 关闭目录closedir(dir);
}

(六)创建符号链接(my_symlink函数)

创建指定源文件到目标文件的符号链接,如果创建过程中出现错误,会打印相应的错误提示。

void my_symlink(const char *src_file, const char *dst_file)
{// 创建符号链接int ret = symlink(src_file, dst_file);// 如果创建失败if (ret == -1){// 打印错误信息perror("symlink");}
}

(七)创建硬链接(my_link函数)

创建指定源文件到目标文件的硬链接,若创建失败则打印错误信息。 

void my_link(const char *src_file, const char *dst_file)
{// 创建硬链接int ret = link(src_file, dst_file);// 如果创建失败if (ret == -1){// 打印错误信息perror("link");}
}

(八)删除文件(my_rm函数)

删除指定的文件,如果删除操作遇到问题,会给出错误提示。

void my_rm(const char *src_file)
{// 删除指定的文件int ret = remove(src_file);// 如果删除失败if (ret == -1){// 打印错误信息perror("remove");}
}

(九)创建空文件(my_touch函数)

创建一个空文件,如果在创建过程中出现错误,会打印错误信息。

void my_touch(const char *src_file)
{// 以写入模式打开指定的文件,如果文件不存在则创建FILE * fp = fopen(src_file, "w");// 如果文件打开失败if (fp == NULL){// 打印错误信息perror("fopen");}
}

(十)移动文件(my_mv函数)

通过先复制文件,然后删除源文件的方式来实现文件的移动操作。 

void my_mv(const char *src_file, const char *dst_file)
{// 先调用复制文件的函数my_cp(src_file, dst_file);// 再删除源文件my_rm(src_file);
}

(十一)主函数(main函数)

主函数的作用是不断获取用户输入的命令和相关参数,根据输入的不同情况(参数数量)调用相应的功能函数来执行具体的文件操作,并处理可能出现的错误。它是整个程序的控制中心,协调各个功能模块的运行,以实现对文件的各种操作模拟

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <grp.h>
#include <pwd.h>#include "mycpcatcd.h"
#include "mylsll.h"
#include "order.h"// 函数:打印命令提示符
void printfCommandPrompt()
{// 定义一个 125 字节大小的缓冲区来存储当前工作目录char buf[125] = {0};// 获取当前工作目录并存储在缓冲区中getcwd(buf, sizeof(buf));// 以特定的格式和颜色打印命令提示符的各个部分printf("\33[1m");  // 加粗字体printf("\033[39;32m");  // 绿色文本printf("linux@ubuntu");  // 用户名部分printf("\033[0m");  // 恢复默认颜色和字体printf("\33[1m");  // 加粗字体printf(":");  // 分隔符printf("\033[0m");  // 恢复默认颜色和字体printf("\33[1m");  // 加粗字体printf("\033[39;34m");  // 蓝色文本printf("%s", buf);  // 打印当前工作目录printf("\033[0m");  // 恢复默认颜色和字体printf("\33[1m");  // 加粗字体printf("$ ");  // 命令输入提示符printf("\033[0m");  // 恢复默认颜色和字体
}// 主函数
int main()
{while (1)  // 无限循环,等待用户不断输入命令{printfCommandPrompt();  // 打印命令提示符// 定义字符数组来存储命令、源文件和目标文件char order[100] = {0};char src_file[100] = {0};char dst_file[100] = {0};char orders[100] = {0};fgets(orders, sizeof(orders), stdin);  // 从标准输入获取用户输入的命令行int i = 0; int count = 0;// 计算用户输入命令中的空格数量while (orders[i]!= '\0'){if (orders[i] ==''){count++;}i++;}char *token = NULL;  // 用于存储分割后的字符串// 根据空格数量进行不同的处理switch(count){case 0:  // 用户输入没有空格token = strtok(orders,"\n");  // 提取命令,去除换行符strcpy(order,token);  // 将提取的命令复制到 order 数组// 根据命令执行相应操作if (strcmp("cd",order) == 0 )  // 如果是切换目录命令{chdir("/home/linux");  // 切换到指定目录}else if (strcmp("ls",order) == 0 )  // 如果是列出目录命令{my_ls("./");  // 列出当前目录内容}else if (strcmp("ll",order) == 0 )  // 如果是详细列出目录命令{my_ll("./");  // 详细列出当前目录内容}else if (strcmp("q",order) == 0 )  // 如果是退出命令{return 0;  // 程序结束}else  // 如果输入的命令不匹配以上已知命令{printf("Input error, please re-enter\n");  // 提示输入错误,重新输入}break;case 1:  // 用户输入有一个空格token = strtok(orders," ");  // 提取命令strcpy(order,token);  // 复制命令token = strtok(NULL,"\n");  // 提取源文件路径strcpy(src_file,token);  // 复制源文件路径// 根据命令和源文件路径执行相应操作if (strcmp("cat",order) == 0 )  // 如果是查看文件内容命令{my_cat(src_file);  // 查看指定文件内容}else if (strcmp("cd",order) == 0 )  // 如果是切换目录命令{my_cd(src_file);  // 切换到指定目录}else if (strcmp("ls",order) == 0 )  // 如果是列出目录命令{my_ls(src_file);  // 列出指定目录内容}else if (strcmp("ll",order) == 0 )  // 如果是详细列出目录命令{my_ll(src_file);  // 详细列出指定目录内容}else if (strcmp("rm",order) == 0 )  // 如果是删除文件命令{my_rm(src_file);  // 删除指定文件}else if (strcmp("touch",order) == 0)  // 如果是创建空文件命令{my_touch(src_file);  // 创建指定空文件}else  // 如果输入的命令不匹配以上已知命令{printf("Input error, please re-enter\n");  // 提示输入错误,重新输入}break;case 2:  // 用户输入有两个空格token = strtok(orders," ");  // 提取命令strcpy(order,token);  // 复制命令token = strtok(NULL," ");  // 提取源文件路径strcpy(src_file,token);  // 复制源文件路径token = strtok(NULL,"\n");  // 提取目标文件路径strcpy(dst_file,token);  // 复制目标文件路径// 根据命令、源文件路径和目标文件路径执行相应操作if (strcmp("cp",order) == 0 )  // 如果是复制文件命令{my_cp(src_file,dst_file);  // 复制文件}else if (strcmp("ln",order) == 0 )  // 如果是创建硬链接命令{my_link(src_file,dst_file);  // 创建硬链接}else if (strcmp("mv",order) == 0 )  // 如果是移动文件命令{my_mv(src_file,dst_file);  // 移动文件}else  // 如果输入的命令不匹配以上已知命令{printf("Input error, please re-enter\n");  // 提示输入错误,重新输入}break;case 3:  // 用户输入有三个空格token = strtok(orders," ");  // 提取命令的前部分strcpy(order,token);  // 复制token = strtok(NULL," ");  // 提取命令的后部分,与前部分拼接strcat(order,token);  // 拼接命令token = strtok(NULL," ");  // 提取源文件路径strcpy(src_file,token);  // 复制源文件路径token = strtok(NULL,"\n");  // 提取目标文件路径strcpy(dst_file,token);  // 复制目标文件路径// 如果是创建符号链接命令if ( strcmp("ln-s",order) == 0 ){my_symlink(src_file,dst_file);  // 创建符号链接}else  // 如果输入的命令不匹配创建符号链接命令{printf("Input error, please re-enter\n");  // 提示输入错误,重新输入}break;default:  // 用户输入的空格数量不符合预期printf("Input error!");  // 提示输入错误}}return 0;
}

 三、数据结构
• 字符数组:如 buf、order、src_file、dst_file 等,用于存储命令、文件路径等字符串信息。

四、错误处理
在每个文件操作函数中,如果文件打开、读取、写入或其他操作失败,都会通过 perror 函数打印出相应的错误信息,以便用户了解操作失败的原因。

五、运行环境
• 操作系统:支持常见的操作系统,如 Windows、Linux 等。
• 编译环境:需要 C 语言编译器,如 GCC 等。

六、测试计划
• 对每个功能函数进行单独的单元测试,输入不同的有效和无效参数,检查返回值和输出结果是否符合预期。
◦ 例如,对于文件复制函数 my_cp,测试不同大小、类型的文件,包括空文件、大文件、文本文件、二进制文件等。
◦ 对于目录操作函数,如 my_ls 和 my_ll,测试不同权限、不同内容的目录。
• 进行综合测试,模拟用户输入各种命令和参数组合,检查程序的整体运行情况。
◦ 包括连续执行多个命令、输入错误的命令格式、在不同的目录下执行命令等场景。

七、维护与扩展
• 未来可以根据需要添加更多的文件操作命令或优化现有功能的性能。
◦ 例如,添加文件重命名功能、支持文件压缩和解压缩等。
• 对错误处理进行进一步的完善,提供更友好的错误提示信息。
◦ 比如,对于一些常见的错误,给出更具体的解决建议。

整体框图

主函数流程图 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch、Easy-es 快速入门 SearchAfterPage分页 若依前后端分离 Ruoyi-Vue SpringBoot

一、环境安装 Elasticsearch ik分词器 1.1 下载解压Elasticsearch-7.x版本&#xff0c;越高越好&#xff0c;低版本有Log4j漏洞&#xff0c;Easy-es目前支持7.x 1.2 IK中文分词器 将对应Elasticsearch版本IK放进文件夹&#xff0c;Elasticsearch-7.6.1&#xff0c;ik对应版…

Postgres 超时 (Timeout) 详解

原文地址 https://www.bytebase.com/blog/postgres-timeout/ PostgreSQL 提供各种超时 (Timeout) 设置&#xff0c;通过控制某些进程的持续时间来帮助管理和优化数据库操作。这些超时对于确保系统的稳定性和性能至关重要&#xff0c;尤其是在高流量或复杂查询的环境中。让我们…

STM32CubeMX生成stm32MP135中断优先级配置错误修正方法

0 修改方法 使用STM32CubeMX生成stm32MP135代码的中断优先级配置错误&#xff0c;将导致所有中断优先级设置不对。 如果设置EXTI0中断优先级为10&#xff0c;在STM32CubeMX中配置如下&#xff1a; 生成的中断优先级配置代码为&#xff1a; 正确写法应该将中断优先级左移3位&…

python从入门到精通:函数

目录 1、函数介绍 2、函数的定义 3、函数的传入参数 4、函数的返回值 5、函数说明文档 6、函数的嵌套调用 7、变量的作用域 1、函数介绍 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现特定功能的代码段。 name "zhangsan"; length len(nam…

二叉树学习笔记

一、树的概念 树是一种非线性的结构&#xff0c;它是由n个有限结点组成的一个具层次关系的集合。&#xff08;像一颗倒着的树&#xff09; 特点&#xff1a; 有一个特殊的结点&#xff0c;称之为根结点&#xff0c;根结点没有前驱结点 除了根节点以外&#xff0c;其余节点别分…

centos 7.9 迁移到 openEuler22.03-LTS-SP3

openEuler移植案例 | 移植操作指南 | openEuler社区官网 cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) 需要两台机器&#xff0c; 不通过原因 在待升级节点检查是否有安装x2openEuler-core时, 发现已经安装了,不能作为升级节点。该节点为&#xff1a; 解…

MySQL中处理JSON数据

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言 在大数据时代&#xff0c;处理和分析结构化与非结构化数据的能力对于企业的成功至关重要。MySQL作为一种广泛使用的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;在应对传统结构化数据方面表现出色。然…

c++每日练习记录第1天

笔记&#xff1a; C 中&#xff0c;isalnum 函数用于检查一个字符是否是字母数字字符&#xff0c;isalnum 函数定义在 头文件中。双指针法&#xff0c;双指针法是一种常用的算法技巧&#xff0c;特别适用于处理数组、字符串等线性数据结构中的问题。这种方法通常涉及到两个指针…

12、springboot3 vue3开发平台-前端-记住我功能实现

文章目录 1. 前端用户信息保存2. 登录页面添加3. 后端实现 1. 前端用户信息保存 使用pinia持久化保存用户名密码 src/stores/remember-me.js // 定义 store import { defineStore } from "pinia" import {reactive} from vueexport const useRememberMeStore defi…

图书管理管理系统 (GUI)

目录 Java程序设计课程设计 图书管理管理系统 一、前言 1 研究背景 2 目的和意义 3编程环境与工具 二、图书管理系统概述 1主要业务流程 三、需求分析与设计 1 系统需求分析 2.功能需求 3.性能需求 4. 安全需求 2 数据库设计 3 界面设计 四、 总…

Solidworks二次开发:通过XYZ点的曲线

在SolidWorks中&#xff0c;通过XYZ点创建曲线是一种根据一组点的坐标生成三维曲线的方法。这种方法适用于需要根据特定点集设计曲线的情况&#xff0c;比如在建模复杂几何形状或执行逆向工程时。在SolidWorks中通过XYZ点创建曲线&#xff0c;操作步骤如下 打开SolidWorks并新建…

利用modelscope下载模型

1. modelscope的简介 ModelScope作为一个先进的“模型即服务”(MaaS)平台&#xff0c;它的核心在于汇聚人工智能领域的尖端模型&#xff0c;降低了在现实世界应用这些前沿技术的门槛。该平台通过ModelScope库展现了其强大功能&#xff0c;这一库专为简化开发者体验而设计&…

Element-06.案例

一.目标 实现下面这个页面&#xff0c;表格中的数据使用axois异步加载数据 二.实现步骤 首先在vue项目的views文件夹中新建一个tlias文件夹&#xff0c;用来存储该案例的相关组件。员工页面组件&#xff08;EmpView.vue&#xff09;和部门页面组件&#xff08;DeptView.vue&…

C语言指针详解-上

C语言指针详解-上 前言1.指针的基本概念1.1指针是什么1.2指针的声明与初始化1.3取地址符&和解引用符*& 运算符用于**获取变量的地址*** 运算符用于访问指针指向的值 2.指针的类型常见数据类型的指针指针与数组、字符串数组指针结构体指针函数指针二级指针void指针 3.指…

【数据结构】二叉树(二)遍历

上篇已经了解对二叉树有了大概了解&#xff0c;本篇学习二叉树的前序、中序、后序及层序遍历的递归与非递归共7种遍历方法&#xff0c;快收藏吧~ 目录 1、前序遍历 递归方式&#xff1a; 迭代方式&#xff1a; 2、中序遍历 递归方式&#xff1a; 迭代方式&#xff1a; …

XXX【4】策略模式

如上图所示&#xff0c;如果要加入一个新的货币&#xff0c;那么就需要对类中的Calculate函数进行修改&#xff0c;这违背了封闭开放原则。 上图中的方式更加合适&#xff0c;搞一个抽象类&#xff08;方法中可以用多态调用&#xff09;&#xff0c;然后每个货币自己是一个类&a…

每日学习笔记:C++ STL之堆栈容器stack

目录 stack定义 核心接口 stack class声明 stack class定义 用户自定义的Stack Class C11特色的插入元素的新形式 运用实例 stack定义 核心接口 stack class声明 stack class定义 用户自定义的Stack Class C11特色的插入元素的新形式 运用实例

数据结构(邓俊辉)学习笔记】优先级队列 07——堆排序

1.算法 作为完全二叉堆的一个应用&#xff0c;这节来介绍堆排序算法。 是的&#xff0c;谈到优先级队列&#xff0c;我们很自然地就会联想到排序。因为就其功能而言&#xff0c;包括完全二叉堆在内的任何一种优先级队列都天生地具有选取功能&#xff0c;也就是选取其中的最大…

【mkdir rmdir】Centos/Linux mkdir rmdir命令详细介绍

【mkdir & rmdir】Centos/Linux mkdir & rmdir命令详细介绍 简介 mkdir rmdir 简介 mkdir 命令和 rmdir 命令是在 linux 当中比较常用的两个命令&#xff0c;这两个命令前者是创建空目录&#xff0c;后者是删除空目录。rmdir 命令的定位比较尴尬它的功能可以被 rm 命…

“论面向服务架构设计及其应用”写作框架,软考高级,系统架构设计师

论文真题 面向服务架构&#xff08;Service-Oriented Architecture, SOA&#xff09; 是一种应用框架&#xff0c;将日常的业务应用划分为单独的业务功能服务和流程&#xff0c;通过采用良好定义的接口和标准协议将这些服务关联起来。通过实施基于SOA的系统架构&#xff0c;用…