title: Linux_4 shell编写
shell
pwd (/root/A/2025_7/19/myshell)
首先需要设计命令行提示 (MakeCommandLine())
首先获取相关信息
getenv(“name”)
// 获取用户名
const char* GetUserName() {const char* name = getenv("USER");if (name == NULL) {return "None";}return name;
}// 获取主机名
const char* GetHostName() {static char hostname[256];if (gethostname(hostname, sizeof(hostname)) == 0) {return hostname;}return "None";
}// 获取当前路径
const char* GetCwd() {const char* cwd = getenv("PWD");if (cwd == NULL) {return "None";}return cwd;
}
制作命令行提示
// 制作命令提示行
void MakeCommandLine() {// 内容char commandline[SIZE];const char* username = GetUserName();const char* hostname = GetHostName();const char* cwd = GetCwd();// printf("%s\n", username);// printf("%s\n", cwd);// printf("%s\n", hostname);// 拼接snprintf(commandline, sizeof(commandline), "[%s@%s %s]> ", username, hostname, cwd);printf("%s", commandline);fflush(stdout);
}
获取用户命令行字符串(GetUserCommand(char usercommand[], size_t n))
使用fgets() 函数
// 获取用户输入字符串
// 返回值 > 0进行后续处理
int GetUserCommand(char usercommand[], size_t n) {char *s = fgets(usercommand, n, stdin);if (s == NULL) {return -1;}// 去掉末尾 换行符usercommand[strlen(usercommand) - 1] = ZERO;return strlen(usercommand);
}
分割用户输入字符串 (SplitCommand(char command[], size_t n))
// 存放命令
char *gArgv[NUM];
// 分割输入命令
void SplitCommand(char command[], size_t n){//ls - a -l -n// SEP " " 表示使用 strtok函数 按照SEP分割字符串gArgv[0] = strtok(command, SEP);int index = 1;while (gArgv[index++] = strtok(NULL, SEP));
}
执行命令 (ExecuteCommand())
执行命令时, 不能自己去执行, 需要子进程去执行
// 执行命令
void ExecuteCommand() {pid_t id = fork();if (id < 0) {exit(1);} else if (id == 0) {// 子进程执行进程替换execvp(gArgv[0], gArgv);exit(errno);} else {int status = 0;pid_t rid = waitpid(id, &status, 0);}
}
第一版 shell 代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>#define ZERO '\0'// 提示行 / ()大小
#define SIZE 512// 分割命令个数
#define NUM 32
// 按照SEP分割字符串
#define SEP " "// 获取用户名
const char* GetUserName() {const char* name = getenv("USER");if (name == NULL) {return "None";}return name;
}// 获取主机名
const char* GetHostName() {static char hostname[256];if (gethostname(hostname, sizeof(hostname)) == 0) {return hostname;}return "None";
}// 获取当前路径
const char* GetCwd() {const char* cwd = getenv("PWD");if (cwd == NULL) {return "None";}return cwd;
}// 制作命令提示行
void MakeCommandLine() {// 内容char commandline[SIZE];const char* username = GetUserName();const char* hostname = GetHostName();const char* cwd = GetCwd();// printf("%s\n", username);// printf("%s\n", cwd);// printf("%s\n", hostname);// 拼接snprintf(commandline, sizeof(commandline), "[%s@%s %s]> ", username, hostname, cwd);printf("%s", commandline);fflush(stdout);
}// 获取用户输入字符串
int GetUserCommand(char usercommand[], size_t n) {char *s = fgets(usercommand, n, stdin);if (s == NULL) {return -1;}// 去掉末尾 换行符usercommand[strlen(usercommand) - 1] = ZERO;return strlen(usercommand);
}// 存放命令
char *gArgv[NUM];
// 分割输入命令
void SplitCommand(char command[], size_t n){//ls - a -l -n// SEP " " 表示使用 strtok函数 按照SEP分割字符串gArgv[0] = strtok(command, SEP);int index = 1;while (gArgv[index++] = strtok(NULL, SEP));
}// 执行命令
void ExecuteCommand() {pid_t id = fork();if (id < 0) {// 创建进程失败exit(1);} else if (id == 0) {// 子进程执行进程替换execvp(gArgv[0], gArgv);exit(errno);} else {// 进程等待int status = 0;pid_t rid = waitpid(id, &status, 0);}
}int main() {while (1) {// 制作一个命令行MakeCommandLine();// 获取用户输入字符串char usercommand[SIZE];int n = GetUserCommand(usercommand, sizeof(usercommand));// 命令行字符串的分割SplitCommand(usercommand, sizeof(usercommand));// 执行命令ExecuteCommand();}return 0;
}
上述代码无法切换路径, 是子进程在切换路径
检查命令是否是内建命令 (CheckBuildir())
char cwd[SIZE];// 执行cd命令
void Cd() {const char* path = gArgv[1];if (path == NULL) {path = GetHome();}// 改变路径chdir(path);// 更新环境变量char temp[SIZE];getcwd(temp, sizeof(temp));snprintf(cwd, sizeof(cwd), "PWD=%s", temp);putenv(cwd);
}// 判断是否内建命令
int CheckBuildir() {int yes = 0;const char* enter_cwd = gArgv[0];// 一个个比较if (strcmp(enter_cwd, "cd") == 0) {yes = 1;Cd();}return yes;
}