Linux系统中模拟实现shell

源代码(C语言)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/types.h>
#define SIZE 512
#define ZERO '\0'
#define NUM 32
#define SEP " "
#define SkipPath(p) do{p+=(strlen(p)-1);while(*p!='/') p--;}while(0) //宏函数,获取路径中的最后一个目录

//获取环境变量中的当前用户名
const char* GetUserName() {
    const char* name = getenv("USER");
    if (name == NULL) return "None";
    return name;
}

//获取环境变量中的主机名
const char* GetHostName() {
    const char* name = getenv("HOSTNAME");
    if (name == NULL) return "None";
    return name;
}

//获取环境变量中的当前路径
const char* GetCwd() {
    const char* cwd = getenv("PWD");
    if (cwd == NULL) return "None";
    return cwd;
}

//输出命令行提示符
void CommandLine() {
    char line[SIZE]; //自定义缓冲区
    const char* username = GetUserName();
    const char* hostname = GetHostName();
    const char* cwd = GetCwd();
    SkipPath(cwd);
    snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd + 1); //命令行提示符中的路径改为相对路径
    printf("%s", line);
    fflush(stdout);
}

//获取用户输入命令
int GetCommand(char command[], size_t n) {
    char* s = fgets(command, n, stdin); //用户输入命令到command中
    if (s == NULL) return -1;
    command[strlen(command) - 1] = ZERO;
    return strlen(command);
}

char* gArgv[NUM]; //接收分割后的命令

//分割一行命令
void SplitCommand(char command[], size_t n) {
    (void)n;
    gArgv[0] = strtok(command, SEP);
    int index = 1;
    while ((gArgv[index++] = strtok(NULL, SEP)));
}

//执行命令
int lastcode = 0;
void ExecuteCommand() {
    pid_t id = fork(); //创建子进程
    if (id < 0) {
        printf("fork failed\n");
        exit(-1);
    } else if (id == 0) { //子进程
        execvp(gArgv[0], gArgv);
        exit(errno);
    } else { //父进程
        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);
        }
    }
}

//获取家目录路径
const char* GetHome() {
    const char* home = getenv("HOME");
    if (home == NULL) return "/";
    return home;
}

char cwd[SIZE * 2];

//检查是否为内建命令
int CheckBuildIN() {
    int yes = 0;
    const char* cmd = gArgv[0];
    if (strcmp(cmd, "cd") == 0) {
        yes = 1;
        const char* path = gArgv[1];
        if (path == NULL) path = GetHome(); //特殊判断,直接执行cd命令会进入家目录
        chdir(path); //切换路径到path路径(path可能是..或者一串路径)
        char temp[SIZE * 2];
        getcwd(temp, sizeof(temp)); //获取最新当前路径,存放在temp中
        snprintf(cwd, sizeof(cwd), "PWD=%s", temp); //将最新的工作路径按"PWD=%s"格式写入cwd中
        putenv(cwd); //更新父进程环境变量,这样命令行提示符中的路径才会变化
    }
    return yes;
}

int main() {
    int quit = 0;
    while (!quit) {
        // 输出命令行提示符
        CommandLine();
        // 获取用户输入命令
        char command[SIZE];
        int n = GetCommand(command, sizeof(command));
        if (n <= 0) return -1; // 用户没有输入命令
        // 分割获取到的一行命令
        SplitCommand(command, sizeof(command));
        //检查是否为内建命令
        n = CheckBuildIN();
        if (n) continue; //如果是内建命令,就让父进程亲自执行,不创建子进程
        // 执行命令
        ExecuteCommand();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南林yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值