Linux——简单的Shell程序

📘北尘_个人主页
🌎个人专栏 :《Linux操作系统》《经典算法试题 》《C++》 《数据结构与算法》

☀️走在路上,不忘来时的初心

文章目录


一、Shell程序思路

用下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表,它随着时间的流逝从左向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。

然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。

  1. 获取命令行
  2. 解析命令行
  3. 建立一个子进程(fork)
  4. 替换子进程(execvp)
  5. 父进程等待子进程退出(wait)
    根据这些思路,和我们前面的学的技术,就可以自己来实现一个shell了。

二、Shell代码展示

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NUM 1024
#define SIZE 64
#define SEP " "
//#define Debug 1

char cwd[1024];
char enval[1024]; // for test
int lastcode = 0;

char *homepath()
{
    char *home = getenv("HOME");
    if(home) return home;
    else return (char*)".";
}

const char *getUsername()
{
    const char *name = getenv("USER");
    if(name) return name;
    else return "none";
}
const char *getHostname()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname) return hostname;
    else return "none";
}
const char *getCwd()
{
    const char *cwd = getenv("PWD");
    if(cwd) return cwd;
    else return "none";
}
int getUserCommand(char *command, int num)
{
    printf("[%s@%s %s]# ", getUsername(), getHostname(), getCwd());
    char *r = fgets(command, num, stdin); // 最终你还是会输入\n
    if(r == NULL) return -1;
    // "abcd\n" "\n"
    command[strlen(command) - 1] = '\0'; // 有没有可能越界?不会
    return strlen(command);
}

void commandSplit(char *in, char *out[])
{
    int argc = 0;
    out[argc++] = strtok(in, SEP);
    while( out[argc++] = strtok(NULL, SEP));

#ifdef Debug
    for(int i = 0; out[i]; i++)
    {
        printf("%d:%s\n", i, out[i]);
    }
#endif
}

int execute(char *argv[])
{
    pid_t id = fork();
    if(id < 0) return -1;
    else if(id == 0) //child
    {
        // exec command
        execvp(argv[0], argv); // cd ..
        exit(1);
    }
    else // father
    {
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0){
            lastcode = WEXITSTATUS(status);
        }
    }

    return 0;
}

void cd(const char *path)
{
    chdir(path);
    char tmp[1024];
    getcwd(tmp, sizeof(tmp));
    sprintf(cwd, "PWD=%s", tmp); // bug
    putenv(cwd);
}

// 什么叫做内键命令: 内建命令就是bash自己执行的,类似于自己内部的一个函数!
// 1->yes, 0->no, -1->err
int doBuildin(char *argv[])
{
    if(strcmp(argv[0], "cd") == 0)
    {
        char *path = NULL;
        if(argv[1] == NULL) path=homepath();
        else path = argv[1];
        cd(path);
        return 1;
    }
    else if(strcmp(argv[0], "export") == 0)
    {
        if(argv[1] == NULL) return 1;
        strcpy(enval, argv[1]);
        putenv(enval); // ???
        return 1;
    }
    else if(strcmp(argv[0], "echo") == 0)
    {
        if(argv[1] == NULL){
            printf("\n");
            return 1;
        }
        if(*(argv[1]) == '$' && strlen(argv[1]) > 1){ 
            char *val = argv[1]+1; // $PATH $?
            if(strcmp(val, "?") == 0)
            {
                printf("%d\n", lastcode);
                lastcode = 0;
            }
            else{
                const char *enval = getenv(val);
                if(enval) printf("%s\n", enval);
                else printf("\n");
            }
            return 1;
        }
        else {
            printf("%s\n", argv[1]);
            return 1;
        }
    }
    else if(0){}

    return 0;
}

int main()
{
    while(1){
        char usercommand[NUM];
        char *argv[SIZE];
        // 1. 打印提示符&&获取用户命令字符串获取成功
        int n = getUserCommand(usercommand, sizeof(usercommand));
        if(n <= 0) continue;
        // 2. 分割字符串
        // "ls -a -l" -> "ls" "-a" "-l"
        commandSplit(usercommand, argv);
        // 3. check build-in command
        n = doBuildin(argv);
        if(n) continue;
        // 4. 执行对应的命令
        execute(argv);
    }
}

相关推荐
大树885 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠5 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush46 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5206 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz6 小时前
Maven依赖冲突
java·服务器·maven
麦聪聊数据7 小时前
数据服务化时代:企业数据能力输出的核心路径
数据库
不会C语言的男孩7 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
shushangyun_7 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
古城小栈7 小时前
Unix 与 Linux 异同小叙
linux·服务器·unix
DARLING Zero two♡7 小时前
【MySQL数据库】数据类型与表约束
数据库·mysql