[Linux]簡單的shell實現

羡鱼OvO發表於2024-11-25

一個簡單的shell實現

什麼是內建命令

內建命令是由shell本身提供的命令,而不是透過外部程式(如位於/bin/usr/bin等目錄下的可執行檔案)來執行的命令。這些命令在shell啟動的時候就載入到記憶體中,執行效率相對較高。

常見的內建命令有:cdpwdecho

什麼是當前路徑

本質就是程序的工作目錄,預設是程序在哪個目錄底下執行,它的工作目錄就是這個目錄。

[Linux]簡單的shell實現

那這個工作目錄可以更改嗎?答案是可以的,使用chdir()函式就可以更改當前程序的工作目錄。

[Linux]簡單的shell實現

在我們自己實現一個簡單的shell,執行cd命令的時候,你會發現路徑沒有變化。那是因為在執行命令的時候,我們是使用fork()出來的子程序來執行的cd,子程序有自己的工作目錄(一般和父程序的工作目錄相同),當執行cd的時候,更改的是子程序的目錄,子程序執行完畢,什麼都沒了,繼續用的是父程序,即shell。

簡單的shell實現

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NUM 1024
#define OP_NUM 64

char command[NUM];
char *myargv[OP_NUM];
int lastCode = 0;
int lastSig = 0;

int main()
{
    while (1)
    {
        char *user = getenv("USER");
        char *host = getenv("HOSTNAME");
        printf("%s@%s 當前路徑#", user, host);
        fflush(stdout);

        char *s = fgets(command, sizeof(command) -1, stdin);//獲取命令列的輸入
        assert(s != NULL);
        (void)s;
        //清除使用者按下的回車
        command[strlen(command) - 1] = 0;

        //字串切割
        myargv[0] = strtok(command, " ");
        int i = 1;

        //顯示顏色
        if (myargv[0] != NULL && strcmp(myargv[0], "ls") == 0) 
        {
            myargv[i++] = (char*)"--color=auto";
        }

        //myargv[] 必須以NULL結尾,而strtok函式結束的時候返回NULL
        //strtok()中的NULL表示從上次結束的位置繼續查詢下一個子串
        while (myargv[i++] = strtok(NULL, " ")) {}

        //不需要建立子程序,讓shell自己執行對應的命令
        if (myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if (myargv[1] != NULL) chdir(myargv[1]);
            continue;
        }
        if (myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if (strcmp(myargv[1], "$?") == 0) printf("%d, %d\n", lastCode, lastSig);
            else printf("%s\n", myargv[1]);
            continue;
        }
        
        //建立子程序
        pid_t id = fork();
        assert(id != -1);

        if (id == 0)
        {
            //程式替換
            execvp(myargv[0], myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        assert(ret > 0);
        (void)ret;

        lastCode = WEXITSTATUS(status);
        lastSig = WIFEXITED(status);

    }
}

相關文章