C系统编程:从零手搓一个shell

news/2024/10/21 10:00:18/

背景

这么久没更新就是在干这件事!!因为系统编程已经学的差不多了,所以想找几个项目练练手,之前就一直想写一个自己的shell!!现在终于有机会实现了。
首先说明一下我的操作系统:Arch linux
服务器操作系统:Ubantu 18.04.6
这两个系统上面我都尝试了我的代码,都可以正常运行!

设计思路

设计shell最难的地方有三个点:

  • 命令的分割
  • 命令的执行方式
  • 重定向和管道与命令执行的关系
    搞清楚这三点,一个shell可以说是手到擒来。

设计过程

在设计之前我先准备了一些用于调试的代码(因为我不会用调试软件,主要如下:
首先是字体颜色的代码:

#ifndef _COLOR_H
#define _COLOR_H#define NONE  "\e[0m"           //清除颜色,即之后的打印为正常输出,之前的不受影响
#define BLACK  "\e[0;30m"  //深黑
#define L_BLACK  "\e[1;30m" //亮黑,偏灰褐
#define RED   "\e[0;31m" //深红,暗红
#define L_RED  "\e[1;31m" //鲜红
#define GREEN  "\e[0;32m" //深绿,暗绿
#define L_GREEN   "\e[1;32m" //鲜绿
#define BROWN "\e[0;33m" //深黄,暗黄
#define YELLOW "\e[1;33m" //鲜黄
#define BLUE "\e[0;34m" //深蓝,暗蓝
#define L_BLUE "\e[1;34m" //亮蓝,偏白灰
#define PINK "\e[0;35m" //深粉,暗粉,偏暗紫
#define L_PINK "\e[1;35m" //亮粉,偏白灰
#define CYAN "\e[0;36m" //暗青色
#define L_CYAN "\e[1;36m" //鲜亮青色
#define GRAY "\e[0;37m" //灰色
#define WHITE "\e[1;37m" //白色,字体粗一点,比正常大,比bold小
#define BOLD "\e[1m" //白色,粗体
#define UNDERLINE "\e[4m" //下划线,白色,正常大小
#define BLINK "\e[5m" //闪烁,白色,正常大小
#define REVERSE "\e[7m" //反转,即字体背景为白色,字体为黑色
#define HIDE "\e[8m" //隐藏
#define CLEAR "\e[2J" //清除
#define CLRLINE "\r\e[K" //清除行
#endif

这样就不用我们在需要输出带有颜色的字体的时候去查询对应的颜色编号了。

例如:如果想要输出一个红色字体的字符:

printf("RED"hello world ! \n"NONE");

我还设计了一个日志函数,用于在程序中记录一些函数的执行信息,这样有利于进行bug调试:

#include "./head.h"
#include "./log_message.h"// 第一个参数是日志等级,第二个参数是文件路径,第三个参数是记录类型(有补充和添加两种) 后面两个参数是用来写入内容的,和printf以及scanf的用法一样。
void log_event(int level, const char *filename, int log_type, const char *format, ...) {time_t now = time(NULL);char *level_str;FILE *fp;va_list args;switch(level) {case LOG_LEVEL_INFO: {level_str = "INFO";break;}case LOG_LEVEL_WARNING: {level_str = "WARNING";break;}case LOG_LEVEL_ERROR : {level_str = "ERROR";break;}default: {level_str = "UNKNOWN";break;}}fp = fopen(filename, "a");if(fp == NULL) {perror("[Log]Open file ERROR!");return ;}if(fp != NULL) {fprintf(fp, "%s [%s]: ", ctime(&now), level_str);va_start(args, format);vfprintf(fp, format, args);va_end(args);fprintf(fp, "\n");fclose(fp);}return ;
}

还有头文件哦:

#ifndef _LOG_MESSAGE_H
#define _LOG_MESSAGE_H#define LOG_LEVEL_INFO 0
#define LOG_LEVEL_WARNING 1
#define LOG_LEVEL_ERROR 2
#define LOG_LEVEL_UNKNOWN 3#define LOGT_add 0
#define LOGT_new 1void log_event(int level, const char *filename, int log_type, const char *format, ...);#endif

接下来是我的主要头文件,所有调用的库以及DBG函数都存放在这个文件中:

#ifndef _HEAD_H
#define _HEAD_H#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <semaphore.h>
#include <libgen.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>//---head file from user#include "color.h"//------------------------------ DBG ------------------------------ 
#ifdef _D/* * DBG : aimed to printf the name and num (enough char, int or other) of the variable.* */#define DBG(fmt, args...) printf(fmt, ##args)// 泛型选择器
#define _AUTO_DBG(arg) _Generic((arg),\int: printf(""#arg" = %d\n", (arg)),\long: printf(""#arg" = %ld\n", (arg)),\long long: printf(""#arg" = %lld\n", (arg)),\float: printf(""#arg" = %f\n", (arg)),\double: printf(""#arg" = %lf\n", (arg)),\long double: printf(""#arg" = %Lf\n", (arg)),\char: printf(""#arg" = %c\n", (arg)),\char*: printf(""#arg" = '%s'\n", (arg)),\default: printf(""#arg" = %p\n", (arg))\
)#define _D_DBG(arg) printf("DBG:"#arg" = %d\n", arg);
#define _F_DBG(arg) printf("DBG:"#arg" = %f\n", arg);
#define _LF_BG(arg) printf("DBG:"#arg" = %lf\n", arg);
#define _LLD_NDBG(arg) printf("DBG:"#arg" = %lld\n", arg);
#define _LLF_DBG(arg) printf("DBG:"#arg" = %llf\n", arg);
#define _S_DBG(arg) printf("DBG:"#arg" = '%s'\n", arg);
#define _C_DBG(arg) printf("DBG:"#arg" = %c\n", arg);
#define _G_DBG(arg) printf("DBG:"#arg" = %g\n", arg);#else#define DBG(fmt, args...)
#define _AUTO_DBG(arg)
#define _D_DBG(arg)
#define _F_DBG(arg)
#define _LF_DBG(arg)
#define _LLD_DBG(arg)
#define _LLF_DBG(arg)
#define _S_DBG(arg)
#define _C_DBG(arg)
#define _G_DBG(arg)#endif#endif

当然,并不是所有的头文件和宏定义都能用的上,这是我学习过程中积累下来的一个头文件,写任何系统编程的程序都会用的到。

值得提及的是DBG这个宏,非常的好用,我只需要在编译时加上_D的参数就可以使得代码中所有的DBG代码全部有效,如果不使用_D,就不会执行,这样以来想要看代码中执行情况时就可以使用这个函数,想要看正常运行的时候就可以不加参数,非常方便!!!

我的所有辅助我进行编程的头文件就是这些了,接下来的两个文件就是我的shell程序的文件了。
因为代码量不大,我没有选择分开文件去设计我的程序,而是放在了一个文件里面,这样当我想要去修改头文件的时候也很方便!
先上.h文件:
myshell.h

#ifndef _MYSHELL_H
#define _MYSHELL_H#define MAX_NAME_LEN        128             // 用户名最大长度
#define MAX_HOST_NAME_LEN   128             // 主机名最大长度
#define MAX_DIRECTORY_LEN   128             // 目录最大长度#define BUFF_LEN            1024            // 普通缓冲长度,用于程序中一些错误信息的存储
#define MAX_BUFF_LEN        64              // 单个命令参数缓冲最大长度,应当与MAX_CMD_LEN保持相同值
#define MAX_CMD_NUM         64              // 最大支持参数个数
#define MAX_CMD_LEN         64              // 单个命令参数最大长度
#define MAX_ENV_LEN         8192            // 维护的环境变量的最大长度/* 内置命令标号 */
#define COMMENDF_EXIT       0               // exit
#define COMMENDF_CD         1               // cd#define Ture 1
#define False 0
#define clear_flush_scanf() while(getchar() != '\n')/** 命令行 **/
const char* COMMAND_EXIT = "exit";
const char* COMMAND_ECHO = "echo";
const char* COMMAND_CD = "cd";
const char* COMMAND_IN = "<";
const char* COMMAND_OUT = ">";
const char* COMMAND_PIPE = "|";char UserName[MAX_NAME_LEN];
char HostName[MAX_HOST_NAME_LEN];
char CurWorDir[MAX_DIRECTORY_LEN];                    // 记录当前工作路径的全路径
char *BaseWorDir;                                     // 记录当前工作路径的最后一层路径
char CmdProSym;// 内置状态码
enum {RESULT_NORMAL,ERROR_FORK,ERROR_COMMAND,ERROR_WRONG_PARAMETER,ERROR_MISS_PARAMETER,ERROR_TOO_MANY_PARAMETER,ERROR_CD,ERROR_SYSTEM,ERROR_EXIT,/* 重定向的错误信息 */ERROR_MANY_IN,ERROR_MANY_OUT,ERROR_FILE_NOT_EXIST,/* 管道的错误信息 */ERROR_PIPE,ERROR_PIPE_MISS_PARAMETER
};/* 参数数量以及参数数组 */
int argc;
char argv[MAX_CMD_NUM][MAX_CMD_LEN];/* ---------------------- init ---------------------- */
int Get_Username(); // 获取当前用户名
int Get_hostname(); // 获取当前主机名
int Get_WorDir(); // 获取当前所在目录
inline void Init_ShellState(); // 执行启动之后的初始化等操作/* 解析命令函数 */
void Get_Commands();
int isPartation(char *buff);
int __get_cmds(char *buff);int isCommandExist(const char *command);
bool isWrapByCitation(const char *buff);
bool isVariable(const char *buff);
bool isWrapByBigPar(const char *buff);
char *getVarInPar(const char *buff);
/* --------------------- call ---------------------- */
int callExit();
int callCd();int __callCommands_Pipe_(int l, int r);
int __callCommands_Redi_(int l, int r);
int callCommands();/* ---------------------- handle --------------------- */bool Cd_result_handle(int result);               // cd命令的结果处理函数
bool Other_Commands_result_handle(int result);#endif

头文件不多做解释,毕竟都是函数声明嘛~~~
接下来说一下我设计程序的主要思路:
因为shell分为内置命令和外置命令,因此我一开始想要将所有的内置命令写出来,而外置命令使用exec函数族去实现,但是我发现我真的想得很简单。

因为有一部分内置命令(如echo,pwd)也有输出的功能,如果直接分成模块来写内置命令的话,在重定向和命令解析的函数中就会很难做…

因此我选择先写cd命令和exit指令,以这两个指令为优先级最高对待,如果命令中存在exit,则直接退出程序,对于cd命令,我们判断后面第二个参数,这样一来,两个内置命令就完成了。

对于剩下的命令,我们统一分成三种情况,存在管道的和存在重定向的以及什么也不存在的。为什么要这样分呢?其实很简单,

每次生出一个新的进程去执行下一段命令,如果是带有管道的命令,则提前通过dup以及dup2等函数将标准输入输出转换一下,使得管道后的程序使用读端,管道前的程序使用写端,执行完程序之后还需要还原标准输入输出文件描述符。整个命令的解析是一种递归的方式。

下面上代码:

#include "./common/head.h"
#include "./common/log_message.h"
#include "./myshell.h"int main() {int result;pid_t pid;int status;Init_ShellState();if ((pid = fork()) == -1) {perror("[fork]ERROR_FORK");exit(ERROR_FORK);}if (pid != 0) {int status;wait(&status);return 0;} else if (pid == 0) {while (true) {printf(RED "%s" NONE "@" BLUE "%s " YELLOW "%s " NONE "%c ", UserName, HostName, BaseWorDir, CmdProSym);Get_Commands();if (argc != 0) {for (int i = 0; i < argc; ++i) {if (strcmp(argv[i], "exit") == 0) {result = callExit();if (ERROR_EXIT == result) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "ERROR EXIT!\n");exit(1);}}}  // run "cd"if (strcmp(argv[0], COMMAND_CD) == 0) {result = callCd();if (Cd_result_handle(result)) exit(ERROR_SYSTEM);} else { /* run "other commands" */result = callCommands();if (Other_Commands_result_handle(result)) exit(ERROR_SYSTEM);}}}}
}/* 获取当前工作目录, 用户名, 主机名*/
inline void Init_ShellState() {Get_WorDir();Get_Username();Get_hostname();if (strcmp("root", UserName) == 0) CmdProSym = '$';else CmdProSym = '%';return ;
}int Get_Username() {char *temp = getenv("USER");if (temp == NULL) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "Feild to get environment of \"USER\"");return ERROR_SYSTEM;}strcpy(UserName, temp);log_event(LOG_LEVEL_INFO, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "UserName = \"USER\" is %s\n", UserName);return RESULT_NORMAL;
}int Get_hostname() {if (gethostname(HostName, MAX_HOST_NAME_LEN) == -1) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "Not found hostname!\n");return ERROR_SYSTEM;}log_event(LOG_LEVEL_INFO, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "Find the hostname : %s\n", HostName);return RESULT_NORMAL;
}int Get_WorDir() {char *result = getcwd(CurWorDir, MAX_DIRECTORY_LEN);if (result == NULL) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{Get_WorDir} : func > getcwd() error!\n");return ERROR_SYSTEM;}log_event(LOG_LEVEL_INFO, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "Path from (getcwd) = {%s}", CurWorDir);BaseWorDir = basename(CurWorDir);if (strcmp(BaseWorDir, UserName) == 0)  BaseWorDir = "~";return RESULT_NORMAL;
}// 得到下一个由两个空格包裹的参数
// 并且存储到buff中
// 如果遇到换行符说明后面没有参数了,返回1
// 如果没有遇到换行符说明后面有参数,返回0
int __get_cmds(char *buff) {int buffsize = 0;char temp;memset(buff, 0, sizeof(buff));while((temp = getchar()) == ' ' && temp != '\n');//DBG(RED "First char = %c\n" NONE, temp);if(temp == '\n') return 1;do {buff[buffsize++] = temp;//DBG(YELLOW "buff[%d] = %c\n" NONE, buffsize - 1, temp);} while((temp = getchar()) != ' ' && temp != '\n');if(temp == '\n') return 1;else return 0;
}void Get_Commands(){char buff[MAX_BUFF_LEN] = {0};// init all argumentsargc = 0;memset(argv, 0, sizeof(argv));while(!__get_cmds(buff)) {DBG(YELLOW "buff = {%s}\n" NONE, buff);strcpy(argv[argc++], buff);}if (strcmp("", buff) != 0) strcpy(argv[argc++], buff);DBG(YELLOW "last buff = {%s}\n" NONE, argv[argc - 1]);return ;
}int callExit() {pid_t cpid = getpid();if (kill(cpid, SIGTERM) == -1) return ERROR_EXIT;else return RESULT_NORMAL;
}int callCd() {int result = RESULT_NORMAL;if (argc == 1) {const char* home = getenv("HOME");//DBG("{callCd} : home : |%s|\n", home);if (home != nullptr) {int ret = chdir(home);//DBG("{callCd} : %d\n", ret);if (ret) {result = ERROR_WRONG_PARAMETER;log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{callCd} : \"cd\" not found directory of user.\n");}} else {result = ERROR_MISS_PARAMETER;}} else if (argc > 2) {result = ERROR_TOO_MANY_PARAMETER;} else {int ret = chdir(argv[1]);//DBG("{callCd} : %d\n", ret);if (ret) result = ERROR_WRONG_PARAMETER;}return result;
}bool Cd_result_handle(int result) {switch (result) {case ERROR_MISS_PARAMETER:fprintf(stderr, "" RED "Error: Miss parameter while using command \"%s\" \n" NONE "", COMMAND_CD);log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{Cd_result_handle} : Miss parameter while using command \"%s\".\n", COMMAND_CD);return false;case ERROR_WRONG_PARAMETER:fprintf(stderr, "" RED "Error: No such path \"%s\".\n" NONE "", argv[1]);log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{Cd_result_handle} : \"%s\" Path not found.\n", COMMAND_CD);return false;case ERROR_TOO_MANY_PARAMETER:fprintf(stderr, "" RED "Error: Too many parameters while using command \"%s\".\n" NONE "", COMMAND_CD);log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{Cd_result_handle} : \"%s\" Too many parameters while using command\n", COMMAND_CD);log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_add, "Usage : cd ./path\n");return false;case RESULT_NORMAL:result = Get_WorDir();if (ERROR_SYSTEM == result) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{Cd_result_handle} : Lost path after cd.\n");return true;} else {log_event(LOG_LEVEL_INFO, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{Cd_result_handle} : cd OK.\n");return false;}default:log_event(LOG_LEVEL_UNKNOWN, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "Unknow what happend at func : {Cd_result_handle}\n");return true;}
}// 执行可能存在管道的命令
int __callCommands_Pipe_(int l, int r) {if (l >= r) return RESULT_NORMAL; // 没有可以执行的命令了int pipeIdx = -1;for (int i = l; i < r; ++i) {if (strcmp(argv[i], COMMAND_PIPE) == 0) {pipeIdx = i;break;}}// 没有管道,执行可能存在重定向的命令if (pipeIdx == -1) {return __callCommands_Redi_(l, r);} else if (pipeIdx + 1 == argc) {// 管道在最末尾return ERROR_PIPE_MISS_PARAMETER;}// 正式执行命令int fds[2];if (pipe(fds) == -1) {return ERROR_PIPE;}int result = RESULT_NORMAL;pid_t pid = vfork();if (pid == -1) {result = ERROR_FORK;} else if (pid == 0) {close(fds[0]);dup2(fds[1], STDOUT_FILENO); // 将这个命令执行的输出输入到写端close(fds[1]);result = __callCommands_Redi_(l, pipeIdx);exit(result);} else {// 父进程等待子进程的退出状态int status;waitpid(pid, &status, 0);int exitCode = WEXITSTATUS(status);if (exitCode != RESULT_NORMAL) {char info[BUFF_LEN] = {0};char line[BUFF_LEN] = {0};close(fds[1]);dup2(fds[0], STDIN_FILENO);close(fds[0]);while (fgets(line, BUFF_LEN, stdin) != NULL) {strcat(info, line);}printf("%s", info); // 打印错误result = exitCode;} else if (pipeIdx + 1 < r) {close(fds[1]);dup2(fds[0], STDIN_FILENO);close(fds[0]);result = __callCommands_Pipe_(pipeIdx + 1, r);}} return result;
}// 不存在管道,调用可能存在重定向的函数
int __callCommands_Redi_(int l, int r) {// 判断命令是否存在if (!isCommandExist(argv[l])) {return ERROR_COMMAND;}int inNum = 0, outNum = 0;// 存储重定向的文件路径char *inFile = NULL, *outFile = NULL;int endIdx = r; // 第一个重定向符号的位置// 寻找重定向符号并且记录for (int i = l; i < r; ++i) {if (strcmp(argv[i], COMMAND_IN) == 0) {++inNum;if (i + 1 < r) {inFile = argv[i + 1];} else return ERROR_MISS_PARAMETER;if (endIdx == r) endIdx = i;} else if (strcmp(argv[i], COMMAND_OUT) == 0) {++outNum;if (i + 1 < r) {outFile = argv[i + 1];} else return ERROR_MISS_PARAMETER;if (endIdx == r) endIdx = i;}}// 非法情况判断if (inNum > 1) { // 输入重定向符超过一个return ERROR_MANY_IN;} else if (outNum > 1) { // 输出重定向符超过一个return ERROR_MANY_OUT;}// 判断文件是否存在if (inNum == 1) {FILE* fp = fopen(inFile, "r");if (fp == NULL) {return ERROR_FILE_NOT_EXIST;}fclose(fp);}int result = RESULT_NORMAL;pid_t pid = vfork();if (pid == -1) {return ERROR_FORK;} else if (pid == 0) {// 这里的两个标准IO已经在外层被重定向过了if (inNum == 1) freopen(inFile, "r", stdin);if (outNum == 1) freopen(outFile, "w", stdout); char *tcommand[BUFF_LEN];for (int i = l; i < endIdx; ++i) {tcommand[i] = argv[i];}tcommand[endIdx] = NULL;// echo 内置命令if (strcmp(tcommand[l], COMMAND_ECHO) == 0) {// 被花括号包裹的情况if(isWrapByCitation(tcommand[l + 1])) {int len = strlen(tcommand[l + 1]);for (int i = 1; i < len - 1; ++i) {fprintf(stdout, "%c", tcommand[l + 1][i]);}fprintf(stdout, "\n");} else if (isVariable(tcommand[l + 1])) {// 存在变量符号的情况char *temp;if (isWrapByBigPar(tcommand[l + 1])) {// 变量名被花括号包裹char *t = getVarInPar(tcommand[l + 1]);temp = getenv(t);free(t); // 及时释放内存} else {// 没有被包裹temp = getenv(tcommand[l + 1] + 1);}if (temp == NULL) {fprintf(stdout, "\n");} else {// 什么也不是的情况,直接输出原话fprintf(stdout, "%s\n", temp);}} else {fprintf(stdout, "%s\n", tcommand[l + 1]);}} else execvp(tcommand[l], tcommand + l); // 执行命令并输出到stdout// 如果执行到这里说明execvp执行失败了,返回错误exit(errno);} else {int status;waitpid(pid, &status, 0);int err = WEXITSTATUS(status);if (err) {printf(RED"Error : %s\n", strerror(err));}}return result;
}char *getVarInPar(const char *buff) {int len = strlen(buff);char *temp = (char *)malloc(sizeof(char) * (len - 3));memset(temp, 0, sizeof(temp));for (int i = 2; i <= len - 2; ++i) {temp[i - 2] = buff[i];}return temp;
}bool isWrapByBigPar(const char *buff) {int len = strlen(buff);if (len == 0) return false;if (buff[1] == '{' && buff[len - 1] == '}') return true;else return false;
}bool isVariable(const char *buff) {if (buff[0] == '$') return true;else return false;
}bool isWrapByCitation(const char *buff) {int len = strlen(buff);if (len == 0) return false;if (buff[0] == '\"' && buff[len - 1] == '\"') return true;else return false;
}int isCommandExist(const char *command) {if (command == NULL || strlen(command) == 0) return false;int result = true;int fds[2];if (pipe(fds) == -1) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{isCommandExist} : pipe(fds) == -1");result = false;} else {int inFd = dup(STDIN_FILENO);int outFd = dup(STDOUT_FILENO);pid_t pid = vfork();if (pid == -1) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{isCommandExist} : vfork fild!");result = false;} else if (pid == 0) {char tcommand[BUFF_LEN];close(fds[0]);dup2(fds[1], STDOUT_FILENO);close(fds[1]);sprintf(tcommand, "command -v %s", command);system(tcommand); // 执行命令检查exit(1);} else {waitpid(pid, NULL, 0);close(fds[1]);dup2(fds[0], STDIN_FILENO);close(fds[0]);if (getchar() == EOF) {log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "{isCommandExist} : command -v Not found!\n");result = false;}dup2(inFd, STDIN_FILENO);dup2(outFd, STDOUT_FILENO);}}return result;
}// 外部函数调用命令
int callCommands() {pid_t pid = fork();if (pid == -1) {return ERROR_FORK;} else if (pid == 0) {// 为了使子进程接管标准输入输出,需要重定向标准输入输出文件符号// 因此需要记录下来int inFd = dup(STDIN_FILENO);int outFd = dup(STDOUT_FILENO);int result = __callCommands_Pipe_(0, argc);dup2(inFd, STDIN_FILENO);dup2(outFd, STDOUT_FILENO);exit(result);} else {// 父进程等待子进程执行命令int status;waitpid(pid, &status, 0);return WEXITSTATUS(status);}return 0;
}bool Other_Commands_result_handle(int result) {switch (result) {case ERROR_FORK:fprintf(stderr, "\e[31;1mError: Fork error.\n\e[0m");log_event(LOG_LEVEL_ERROR, "/home/royi/1.OS_programing/9.myshell/log.txt", LOGT_new, "result from {callCommands} : vfork() faild!\n");return true; // exitcase ERROR_COMMAND:fprintf(stderr, "\e[31;1mError: Command not exist in myshell.\n\e[0m");return false;case ERROR_MANY_IN:fprintf(stderr, "\e[31;1mError: Too many redirection symbol \"%s\".\n\e[0m", COMMAND_IN);return false;case ERROR_MANY_OUT:fprintf(stderr, "\e[31;1mError: Too many redirection symbol \"%s\".\n\e[0m", COMMAND_OUT);return false;case ERROR_FILE_NOT_EXIST:fprintf(stderr, "\e[31;1mError: Input redirection file not exist.\n\e[0m");return false;case ERROR_MISS_PARAMETER:fprintf(stderr, "\e[31;1mError: Miss redirect file parameters.\n\e[0m");return false;case ERROR_PIPE:fprintf(stderr, "\e[31;1mError: Open pipe error.\n\e[0m");return false;case ERROR_PIPE_MISS_PARAMETER:fprintf(stderr, "\e[31;1mError: Miss pipe parameters.\n\e[0m");return false;}return 0;
}

下面是一些执行结果:


http://www.ppmy.cn/news/1434879.html

相关文章

晨昏线学习

看视图记得标自转方向

cd /op-bash: 无法为立即文档创建临时文件: 设备上没有空间

问题 在shell输入命令按tab键时出现以下报错 (base) [link999hadoop102 ~]$ cd /op-bash: 无法为立即文档创建临时文件: 设备上没有空间 -bash: cd: /op: 没有那个文件或目录原因分析 磁盘空间不够 df -Th # 通过命令查看具体情况解决 1、清理大文件 进入到 容量-已用 使…

上海计算机学会 2024年4月月赛 丙组T1 最大公约数

第一题&#xff1a;T1最大公约数 标签&#xff1a; g c d gcd gcd题意&#xff1a;求 a a a和 b b b的最大公约数&#xff08; 1 ≤ a , b ≤ 1 , 000 , 000 , 000 1≤a,b≤1,000,000,000 1≤a,b≤1,000,000,000&#xff09;题解&#xff1a;辗转相除法 g c d ( a , b ) g c …

循环队列中学习

循环队列中&#xff0c;由于入队时尾指针向前追赶头指针;出队时头指针向前追赶尾指针&#xff0c;造成队空和队满时头尾指针均相等。因此&#xff0c;无法通过条件frontrear来判别队列是"空"还是"满"。 解决这个问题的方法至少有两种: ① 另设一布尔变量…

测试用例设计方法-探索性测试

生活犹如骑单车&#xff0c;唯有前进才能保持平衡。大家好&#xff0c;今天给大家分享一下关于探索性测试的方法&#xff0c;在探索性测试中更加考验测试人员的经验&#xff0c;所以我们在平时的测试工作中一定要多记录、多总结、多复盘&#xff0c;对于经常出现的bug深究其根本…

【JS】js数字转k、w结尾 | 1000 = 1k

问题 数字转k、w结尾 如&#xff1a;10001k 100001w 码 /*** 数字转k,w* param {Number} num * returns String*/ const numberTokw num > {if (num < 1000) return numlet endStr w,numVal 10000;if (num > 999 && num < 10000) {endStr knumVal …

VR全景:为户外游玩体验插上科技翅膀

随着VR全景技术的愈发成熟&#xff0c;无数人感到惊艳&#xff0c;也让各行各业看到了一片光明的发展前景。尤其是越来越多的文旅景区开始引入VR全景技术&#xff0c;相较于以往的静态风景图&#xff0c;显然现在的VR全景结合了动态图像和声音更加吸引人。 VR全景技术正在逐步改…

️️️Vue3+Element-Plus二次封装一个可定制化的table组件

前言 为什么需要二次封装 开发后台管理系统,会接触到很多表格和表单,一但表格表单多起来,仅仅只需要一小部分改变&#xff0c;都需要在中重写一大堆代码,许多重复逻辑,我们可以把重复逻辑抽离出来二次封装一个组件 使用,减少在开发中需要编写的代码。 为什么需要定制化 每个…