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;
}