首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言簡易實現 Linux Shell:內建命令與外部程式執行機制

     編輯:關於C

前言

Linux Shell 本質是「命令解析器」:讀取用戶輸入 → 解析命令與參數 → **內建命令**直接執行、**外部命令** fork+exec 執行。本文實現一個極簡 Shell (MyShell),支援 cd 內建命令、任意外部命令執行、提示符顯示(用戶@主機:路徑$/#),代碼精簡易懂,適合學習進程控制與命令行解析。

一、核心架構


while (1) {
    print_prompt();           // 顯示 user@host:path$ 
    read_command();           // 讀取一行
    parse_command();          // 分割 argv[]
    if (builtin) exec_builtin(); // cd 等
    else {
        pid = fork();
        if (pid == 0) execvp(cmd, argv); // 子進程執行外部程式
        else wait(&status);              // 父進程等待
    }
}

二、完整代碼


#include 
#include 
#include 
#include 
#include 

#define MAXSIZE 100

void info_print() { // 顯示提示符
    char buf[MAXSIZE], *username, hostname[MAXSIZE], pwd[MAXSIZE];
    char ch;
    username = getenv("USER");
    getcwd(pwd, MAXSIZE);
    gethostname(hostname, MAXSIZE);
    ch = (strcmp(username, "root") == 0) ? '#' : '$';
    sprintf(buf, "%s@%s:%s%c ", username, hostname, pwd, ch);
    fprintf(stdout, "%s", buf);
}

int main() {
    while (1) {
        char command[MAXSIZE];
        pid_t pid; int status;
        info_print();
        memset(command, 0, MAXSIZE);
        fgets(command, MAXSIZE, stdin); // 比 gets 安全
        command[strcspn(command, "\n")] = 0; // 去換行

        if ((pid = fork()) < 0) { perror("fork"); exit(1); }
        if (pid > 0) { // 父進程
            wait(&status);
        } else { // 子進程
            // 簡易解析:按空格分割 argv
            char *argv[10]; int argc = 0;
            char *token = strtok(command, " ");
            while (token && argc < 10) {
                argv[argc++] = token;
                token = strtok(NULL, " ");
            }
            argv[argc] = NULL;
            if (argc == 0) exit(0); // 空命令

            // 內建命令 cd
            if (strcmp(argv[0], "cd") == 0) {
                if (argc < 2) fprintf(stderr, "cd: missing argument\n");
                else if (chdir(argv[1]) < 0) perror("chdir");
                exit(0);
            }
            // 外部命令
            if (execvp(argv[0], argv) < 0) {
                fprintf(stderr, "%s: command not found\n", argv[0]);
                exit(1);
            }
        }
    }
    return 0;
}

三、關鍵技術點解析

四、擴展方向

  • 更多內建命令:exit, pwd, export, history, jobs。
  • 管道支援:pipe() + 多 fork 串聯標準輸入輸出。
  • 重導向:解析 <, >, 2>,dup2() 替換 stdin/stdout/stderr。
  • 信號處理:SIGINT (Ctrl+C) 不殺 Shell,只殺前台作業;SIGCHLD 非阻塞回收。
  • 命令行編輯:整合 readline 庫支援歷史、補全。

五、總結

這個 <100 行的 MyShell 涵蓋了 Shell 的**靈魂三件套**:提示符→解析→fork/exec/wait。理解它,就掌握了 Unix 進程模型、命令執行機制、Shell 內建與外部命令區別的核心邏輯。在此基礎上逐步擴展,即可構建功能完整的教學級 Shell。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
技術點實現細節
命令讀取fgets 替代不安全的 gets,手動去換行
命令解析strtok 按空格分割,構建 argv[],以 NULL 結尾
內建命令cd 必須在 Shell 進程內執行(改變進程 CWD),不能 fork
外部命令fork() 創建子進程 → execvp() 替換映像,搜尋 PATH
父進程等待wait(&status) 回收子進程,避免殭屍進程
提示符getenv("USER"), getcwd(), gethostname() 組合