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

posted @ 2025-07-21 16:55  _nilv  阅读(12)  评论(0)    收藏  举报