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

背景

这么久没更新就是在干这件事!!因为系统编程已经学的差不多了,所以想找几个项目练练手,之前就一直想写一个自己的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.mzph.cn/web/2997.shtml

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

相关文章

函数的查询

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 在实际使用中经常会需要查询数据库中已有的函数或者某一个函数的内容&#xff0c;下面就介绍一下如何查询函数。 和存储过程类似&#xff0c;这也需要使用到数据字典user_s…

Spring - 4 ( 11000 字 Spring 入门级教程 )

一&#xff1a;Spring IoC&DI 在前⾯的章节中, 我们学习了 Spring Boot 和 Spring MVC 的开发, 可以完成⼀些基本功能的开发了, 但是什么是 Spring 呢? Spring, Spring Boot 和 SpringMVC 又有什么关系呢? 咱们还是带着问题去学习.我们先看什么是Spring 1.1 Spring 是什…

更新至2022年上市公司数字化转型数据合集(四份数据合集)

更新至2022年上市公司数字化转型数据合集&#xff08;四份数据合集&#xff09; 一、2000-2022年上市公司数字化转型数据&#xff08;年报词频、文本统计&#xff09; 二、2007-2022年上市公司数字化转型数据&#xff08;年报和管理层讨论&#xff09;&#xff08;含原始数据…

微前端是如何实现作用域隔离的?

微前端是如何实现作用域隔离的&#xff1f; 一、前言 沙箱&#xff08;Sandbox&#xff09;是一种安全机制&#xff0c;目的是让程序运行在一个相对独立的隔离环境&#xff0c;使其不对外界的程序造成影响&#xff0c;保障系统的安全。作为开发人员&#xff0c;我们经常会同沙…

UE5 GAS开发P35,36,37,38,39 将药水修改为AbilitySystem效果

这几节课都是将药水修改成更方便使用的AbilitySystem效果的Actor,分别为增加血量,增加蓝量,暂时获得最大生命值上限 AuraEffectActor.h // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #…

介绍一个开源IOT组态项目

项目介绍 金合可视化平台是一款强大而操作简便的低代码平台&#xff0c;专为满足物联网领域的可视化开发需求而设计。通过该平台&#xff0c;用户可以利用拖拽配置的方式&#xff0c;轻松创建个性化的可视化大屏&#xff0c;无需熟练的编程技能&#xff0c;大幅提高了开发效率。…

图搜索的经典启发式算法A星(A*、A Star)算法详解

文章目录 1. 引言2. 广度优先搜索3. Dijkstra 算法4. 启发式优先搜索&#xff08;Heuristic&#xff09;4.1 贪心最佳优先搜索4.2 A*搜索 1. 引言 在许多场景中&#xff0c;我们常会遇到一类问题&#xff0c;即“找到一个位置到另一个位置的距离最短&#xff08;用时最少&…

使用 Rust 后,我​​使用 Python 的方式发生了变化

使用 Rust 后&#xff0c;我​​使用 Python 的方式发生了变化 Using type hints where possible, and sticking to the classic “make illegal state unrepresentable” principle. 尽可能使用类型提示&#xff0c;并坚持经典的“使非法状态不可表示”原则。 近年来&#xff…

【Pytorch】(十三)PyTorch模型部署: TorchScript

文章目录 &#xff08;十三&#xff09;PyTorch模型部署Pytorch动态图的优缺点TorchScriptPytorch模型转换为TorchScripttorch.jit.tracetorch.jit.scripttrace和script的区别总结script 和 trace 混合使用保存和加载模型 &#xff08;十三&#xff09;PyTorch模型部署 Pytorc…

科学高效备考AMC8和AMC10竞赛,吃透2000-2024年1850道真题和解析

如何科学、有效地备考AMC8、AMC10美国数学竞赛&#xff1f;多做真题&#xff0c;吃透真题是科学有效的方法之一&#xff0c;通过做真题&#xff0c;可以帮助孩子找到真实竞赛的感觉&#xff0c;而且更加贴近比赛的内容&#xff0c;可以通过真题查漏补缺&#xff0c;更有针对性的…

成功解决ImportError: cannot import name ‘builder‘ from ‘google.protobuf.internal

成功解决ImportError: cannot import name builder from google.protobuf.internal 目录 解决问题 解决思路 解决方法 解决问题 ImportError: cannot import name builder from google.protobuf.internal 解决思路 导入错误:无法从“google.protobuf.internal”导入名称“…

在React函数组件中使用错误边界和errorElement进行错误处理

在React 18中,函数组件可以使用两种方式来处理错误: 使用 ErrorBoundary ErrorBoundary 是一种基于类的组件,可以捕获其子组件树中的任何 JavaScript 错误,并记录这些错误、渲染备用 UI 而不是冻结的组件树。 在函数组件中使用 ErrorBoundary,需要先创建一个基于类的 ErrorB…

网络通信安全

一、网络通信安全基础 TCP/IP协议简介 TCP/IP体系结构、以太网、Internet地址、端口 TCP/IP协议简介如下&#xff1a;&#xff08;from文心一言&#xff09; TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff0c;传输控制协议/网际协议&#xff0…

用友NC Cloud importhttpscer接口任意文件上传漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 一、漏洞描述 用友NC Cloud的importhttpscer接口如果存在任意文件上传…

开源文本嵌入模型M3E

进入正文前&#xff0c;先扯点题外话 这两天遇到一个棘手的问题&#xff0c;在用 docker pull 拉取镜像时&#xff0c;会报错&#xff1a; x509: certificate has expired or is not yet valid 具体是下面&#x1f447;这样的 rootDS918:/volume2/docker/xiaoya# docker pul…

恒峰智慧科技—森林守护者:森林消防泵如何助力灭火?

在茂密的森林中&#xff0c;一场突如其来的火灾可能带来无法估量的破坏。幸运的是&#xff0c;森林消防泵的出现&#xff0c;帮助我们对抗这些威胁。本文将深入探讨森林消防泵如何在灭火工作中发挥重要作用。 一、森林消防泵的功能和重要性&#xff1a; 首先&#xff0c;我们需…

探索人工智能的边界:GPT 4.0与文心一言 4.0免费使用体验全揭秘!

探索人工智能的边界&#xff1a;GPT与文心一言免费试用体验全揭秘&#xff01; 前言免费使用文心一言4.0的方法官方入口进入存在的问题免费使用文心一言4.0的方法 免费使用GPT4.0的方法官方入口进入存在的问题免费使用GPT4.0的方法 前言 未来已来&#xff0c;人工智能已经可以…

Matlab|基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理

1 主要内容 该程序复现《基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理》模型&#xff0c;建立运营商和多虚拟电厂的一主多从博弈模型&#xff0c;研究运营商动态定价行为和虚拟电厂能量管理模型&#xff0c;模型为双层&#xff0c;首先下层模型中&#xff0c;构建…

【Android】android 10 jar_sdk_library添加

前言 当前项目遇到客户&#xff0c;Android 10 平台&#xff0c;需要封装jar_sdk_library给第三方应用使用。其中jar_sdk_library中存在aidl文件。遇到无法编译通过问题。 解决 system/tools/aidl修改 Android.bp修改

frp改造Windows笔记本实现家庭版免费内网穿透

文章目录 前言frp原理Windows服务端IP检验IP固定软件下载端口放行端口映射开机启动 NAS客户端端口查询软件下载端口检验穿透测试自启设置 Ubuntu客户端软件下载后台启动 后记 前言 之前一直用花生壳远程控制一个服务器&#xff0c;但最近内网的网络策略似乎发生了变化&#xf…