【Linux笔记】Linux自定义Shell
发布时间:2026/10/1 9:46:38来源:尧图网络
一、自定义shell1.1 myshell.h#ifndef __MYSHELL_H__ #define __MYSHELL_H__ //在所有系统头文件之前定义,暴露 getenv/setenv 等的正确声明 #define _DEFAULT_SOURCE #include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/wait.h #include sys/types.h #include fcntl.h #include ctype.h //命令行长度 #define COMMAND_SIZE 1024 //命令行格式 #define FORMAT [%s%s %s]# //定义字符缓冲区用于获取真实的目录路径 #define MaxBuf_SIZE 1024 //获取分割字符 ---命令行参数表 #define MAXARGV_SIZE 128 //定义重定向符号的宏 #define NONE_REDIR 0 #define INPUT_REDIR 1 #define OUTPUT_REDIR 2 #define APPEND_REDIR 3 //记录重定向文件 #define MAXFN_SIZE 1024 //全局变量声明 extern char buf[MaxBuf_SIZE]; extern char* g_argv[MAXARGV_SIZE]; extern int g_argc; extern int g_redir; extern char filename[MAXFN_SIZE]; //函数声明 //获取用户名 const char* GetUserName(); //获取主机名 const char* GetHostName(); //获取当前目录的路径 //注getenv(PWD)读取的是进程启动时从父进程继承的环境变量。它只是一份静态的快照不会因为你调用了 chdir() 而自动更新。 const char* GetPwd(); //只显示当前用户路径不显示全路径 const char* DirName(const char* route); //将命令行提示词输出到 字符串cmd_prompt void MakeCommandLine(char cmd_prompt[], int size); //打印命令行提示词 void PrintCommandPrompt(); //获取用户输入的命令 int GetCommandLine(char *out,int size); //分割命令行参数 ls -l -a - ls -l -a int ParseCommandLine(char* commandline); //判定是否出现重定向操作 void RedirCheck(char* commandline); //测试字符串重定向符号和filename是否被正确分割 void TestRedirCheck(); //测试字符串分割 void TestCmd(); //cd命令的执行 void Execd(); //判断内键命令例如cd int CheckAndExecBuiltin(); //执行命令 int Execute(); #endif 1.2 myshell.c#include myshell.h // 命令分割的分隔符(实现内部使用,不对外暴露) #define SEP // 全局变量定义 char buf[MaxBuf_SIZE]; char *g_argv[MAXARGV_SIZE]; // 定义分隔符的数量 int g_argc; // 判定是否出现重定向操作 int g_redir NONE_REDIR; char filename[MAXFN_SIZE]; // 获取用户名 const char *GetUserName() { const char *name getenv(USER); return name NULL ? None : name; } // 获取主机名 const char *GetHostName() { static char hostname[128]; if (gethostname(hostname, sizeof(hostname)) ! 0) return None; hostname[sizeof(hostname) - 1] \0; return hostname; } // 获取当前目录的路径 // getenv(PWD)读取的是进程启动时从父进程继承的环境变量。它只是一份静态的快照不会因为你调用了 chdir() 而自动更新。 const char *GetPwd() { // const char* pwdgetenv(PWD); const char *pwd getcwd(buf, sizeof(buf)); return pwd NULL ? None : pwd; } // 只显示当前用户路径不显示全路径 const char *DirName(const char *route) { if (!route || *route \0) return .; const char *slash strrchr(route, /); if (slash route *(slash 1) \0) return /; return slash ? slash 1 : route; } // 将命令行提示词输出到 字符串cmd_prompt void MakeCommandLine(char cmd_prompt[], int size) { snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd())); } // 打印命令行提示词 void PrintCommandPrompt() { char prompt[COMMAND_SIZE]; MakeCommandLine(prompt, sizeof(prompt)); printf(%s, prompt); fflush(stdout); } // 获取用户输入的命令 int GetCommandLine(char *out, int size) { // 获取用户输入的指令 char *str fgets(out, size, stdin); if (str NULL) return 0; // 清理\n str[strlen(str) - 1] \0; return 1; } // 分割命令行参数 ls -l -a - ls -l -a int ParseCommandLine(char *commandline) { g_argc 0; char *token strtok(commandline, SEP); while (token ! NULL g_argc MAXARGV_SIZE) { g_argv[g_argc] token; token strtok(NULL, SEP); } g_argv[g_argc] NULL; return g_argc 0 ? 1 : 0; } // 判定是否出现重定向操作 void RedirCheck(char *commandline) { // 初始化操作 g_redir NONE_REDIR; filename[0] \0; char *p; // 遍历字符串的指针 char *name_start; // 文件名的起始位置 int name_len; if (commandline NULL) { return; } p commandline; while (*p ! \0) { // 处理单引号 和 双引号 if (*p || *p \) { // 记录引号的类型 char quote *p; // 寻找闭合的引号防止出现在输入的字符串中 echo hello world p; while (*p ! \0 *p ! quote) { p; } // 找到闭合引号进行寻找 重定向符号 、 和 if (*p ! \0) p; continue; } if (*p ) { g_redir INPUT_REDIR; *p \0; p; break; } if (*p ) { *p \0; // 截断原字符串 commandline // 在输入重定向的基础上进一步检测是否为追加重定向 p; if (*p ) { g_redir APPEND_REDIR; p; } else { g_redir OUTPUT_REDIR; } break; } p; } // 如果没有找到重定向符号直接提前返回 if (g_redir NONE_REDIR) { return; } // 清除空格 // echo hello filename while (*p ! \0 isspace((unsigned char)*p)) { p; } if (*p \0) { fprintf(stderr, 语法错误: 重定向符号后缺少文件名\n); g_redir NONE_REDIR; return; } // 获取文件名 // 向后扫描直到遇到空格或字符串结尾确定文件名的结束位置 name_start p; while (*p ! \0 !isspace((unsigned char)*p)) { p; } // 文件名的长度 name_len (int)(p - name_start); // 如果文件名的长度超过了缓冲区的长度则拷贝字符数最多为filename长度-1需要留一个字符\0 if (name_len (int)sizeof(filename)) { name_len (int)sizeof(filename) - 1; } // 拷贝到filename中 strncpy(filename, name_start, name_len); filename[name_len] \0; *name_start \0; } // 测试字符串重定向符号和filename是否被正确分割 void TestRedirCheck() { printf(g_redir: %d,filename: %s\n, g_redir, filename); } // 测试字符串分割 void TestCmd() { for (int i 0; i g_argc; i) { printf(g_argv[%d]-%s\n, i, g_argv[i]); } printf(g_argc:%d\n, g_argc); } // cd命令的执行 void Execd() { const char *path g_argv[1]; if (path NULL) // 1.执行cd命令 { path getenv(HOME); if (path NULL) { fprintf(stderr, cd: HOME 环境变量未设置\n); return; } } else if (strcmp(path, -) 0) // 2.执行cd - 命令 { path getenv(OLDPWD); if (path NULL) { fprintf(stderr, cd: OLDPWD 未设置\n); return; } // 回显当前路径 printf(%s\n, path); } // 3.执行 cd path 命令 // 记录旧目录 char oldpwd[4096]; if (getcwd(oldpwd, sizeof(oldpwd)) NULL) { oldpwd[0] \0; } // 更新当前目录路径 if (chdir(path) ! 0) { perror(cd); return; } // 更新环境变量 char newpwd[4096]; if (oldpwd[0] ! \0) { setenv(OLDPWD, oldpwd, 1); } if (getcwd(newpwd, sizeof(newpwd)) ! NULL) { setenv(PWD, newpwd, 1); } } // 判断内键命令例如cd int CheckAndExecBuiltin() { const char *cmd g_argv[0]; if (cmd NULL) return 0; if (strcmp(cmd, cd) 0) { Execd(); return 1; } return 0; } // 执行命令 int Execute() { pid_t id fork(); if (id 0) { perror(fork fail); exit(1); } else if (id 0) { int fd -1; if (g_redir INPUT_REDIR) { fd open(filename, O_RDONLY, 0666); if (fd 0) exit(1); dup2(fd, 0); close(fd); } else if (g_redir OUTPUT_REDIR) { fd open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd 0) exit(2); dup2(fd, 1); close(fd); } else if (g_redir APPEND_REDIR) { fd open(filename, O_CREAT | O_WRONLY | O_APPEND, 0666); if (fd 0) exit(3); dup2(fd, 1); close(fd); } // 子进程进行程序替换 execvp(g_argv[0], g_argv); // 执行失败打印提示词 perror(g_argv[0]); exit(127); } else { // 父进程回收子进程pid pid_t rid waitpid(id, NULL, 0); if (rid 0) perror(waitpid fail); } return 0; }1.3 main.c#include myshell.h int main() { while (1) { // 1.打印命令行提示词 PrintCommandPrompt(); // 2.获取用户输入的命令 char commandline[COMMAND_SIZE]; if (!GetCommandLine(commandline, sizeof(commandline))) continue; // 3.判定是否出现重定向操作,出现了进行对输入的命令行进行分割 RedirCheck(commandline); // TestRedirCheck(); // 4.命令行分析分割字符串,将分割的字符串存放在全局变量g_argv //ls -l -a - ls -l -a if (!ParseCommandLine(commandline)) continue; // TestCmd(); //.执行内建命令 if (CheckAndExecBuiltin()) continue; // 子进程执行用户输入的命令 Execute(); } return 0; }
网站建设高端定制企业官网