shell的编写
- IT业界
- 2025-07-21 23:03:01

文章目录 1.框架2.命令行3.获取用户命令字符串4.命令行字符串分割5.执行命令和内建命令6.完整代码:
1.框架
我们知道shell是一直存在的,所以首先我们第一步就是要搭建一个框架,使其一直存在。 那么也很简单,一个while循环就可以完成。
2.命令行我们的shell最前面都是有一个命令行的 。如下图:
那么命令行我们应该怎么获取呢?我们可以从环境变量中获取,env查看环境变量。 getenv函数就可以帮我们获取环境变量,可以获取用户名、地址等等! getenv获取成功就会取到那个对象,如果获取失败就会返回空!! 然后我们需要把上面获取到的三个字符拼接在一起,这时候就需要用到snprintf函数了:
到这里,第一步就结束了!
3.获取用户命令字符串这一步我们需要获取,命令字符串,因为有空格。我们无法使用scanf。这里使用fgets
4.命令行字符串分割分割思路: 具体分割我们可以使用strtok函数 但是这里有个需要注意的地方,strtok的第二参数需要是一个字符串!!
5.执行命令和内建命令内建命令:
6.完整代码:以上就是编写shell的大致思路了!上面的shell为了和系统本身的shell做区分,所以前的路径都是绝对路径,一长串。 myshell.c:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #define SIZE 512 #define ZERO '\0' #define SEP " " #define NUM 32 // 为了方便,我就直接定义了 char cwd[SIZE*2]; char *gArgv[NUM]; int lastcode = 0; void Die() { exit(1); } //获取家目录 const char *GetHome() { const char *home = getenv("HOME"); if(home == NULL) return "/"; return home; } //获取用户名 const char *GetUserName() { const char *name = getenv("USER"); if(name == NULL) return "None"; return name; } //获取主机名 const char *GetHostName() { const char *hostname = getenv("HOSTNAME"); if(hostname == NULL) return "None"; return hostname; } //获取路径 const char *GetCwd() { const char *cwd = getenv("PWD"); if(cwd == NULL) return "None"; return cwd; } int GetUserCommand(char command[], size_t n) { char *s = fgets(command, n, stdin); if(s == NULL) return -1; command[strlen(command)-1] = ZERO; return strlen(command); } void MakeCommandLineAndPrint() { char line[SIZE]; const char *username = GetUserName(); const char *hostname = GetHostName(); const char *cwd = GetCwd(); snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd); printf("%s", line); fflush(stdout); } void SplitCommand(char command[], size_t n) { (void)n; // "ls -a -l -n" -> "ls" "-a" "-l" "-n" gArgv[0] = strtok(command, SEP); int index = 1; while((gArgv[index++] = strtok(NULL, SEP))); // done, 故意写成=,表示先赋值,在判断.因为分割之后,如果无法分割strtok会返回NULL,刚好让gArgv最后一个元素是NULL, 并且while判断结束 } void ExecuteCommand() { pid_t id = fork(); if(id < 0) Die(); else if(id == 0) { // child execvp(gArgv[0], gArgv); exit(errno); } else { // fahter int status = 0; pid_t rid = waitpid(id, &status, 0); if(rid > 0) { lastcode = WEXITSTATUS(status); if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode); } } } void Cd() { const char *path = gArgv[1]; if(path == NULL) path = GetHome(); // path 一定存在 chdir(path); // 刷新环境变量 char temp[SIZE*2]; getcwd(temp, sizeof(temp)); snprintf(cwd, sizeof(cwd), "PWD=%s", temp); putenv(cwd); // OK } int CheckBuildin() { int yes = 0; const char *enter_cmd = gArgv[0]; if(strcmp(enter_cmd, "cd") == 0) { yes = 1; Cd(); } else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0) { yes = 1; printf("%d\n", lastcode); lastcode = 0; } return yes; } int main() { //首先,自己写的shell需要一直存在,所以设置一个while循环 int quite=0; while(!quite) { // 1. 我们需要自己输出一个命令行 MakeCommandLineAndPrint(); // 2. 获取用户命令字符串 char usercommand[SIZE]; int n = GetUserCommand(usercommand, sizeof(usercommand)); if(n <= 0) return 1; // 3. 命令行字符串分割. SplitCommand(usercommand, sizeof(usercommand)); // 4. 检测命令是否是内建命令 n = CheckBuildin(); if(n) continue; // 5. 执行命令 ExecuteCommand(); } return 0; }