簡易shell

目錄

一、整體功能概述

函數準備

1.env命令

2.getenv()函數

3.snprintf

4.strtok()函數

三、全局變量

四、核心功能函數解析

1.?信息獲取函數

2.?命令行交互

3.?命令解析

4.?普通命令執行

5.?內置命令處理(核心功能)

五、主函數流程

六、總代碼


一、整體功能概述

這是一個增強版的命令行解釋器,具有以下功能:

  • 顯示彩色命令行提示符
  • 支持內置命令(cd、export、echo)
  • 支持環境變量操作
  • 記錄上一條命令的退出狀態
  • 為 ls 命令自動添加顏色選項

函數準備

1.env命令

輸出對應的環境變量

2.getenv()函數

這是一個獲取操作系統環境變量的函數

所需參數:

#include<stdlib.h>
char *getenv(const char *name);//獲取當前進程環境變量值

代碼:

結果:

3.snprintf

snprintf 是一個安全版本的字符串格式化函數,用于將格式化的數據寫入字符串緩沖區,同時防止緩沖區溢出。

函數參數:

int snprintf(char *str, size_t size, const char *format, ...);

參數說明:

str:目標字符串緩沖區

size:緩沖區大小(包括結尾的 null 字符)

format:格式化字符串

...:可變參數列表

返回值:

成功:返回想要寫入的字符數(不包括結尾的 null)

失敗:返回負值

例子:

char buffer[50];
int n = snprintf(buffer, sizeof(buffer), "Hello, %s!", "World");
// buffer = "Hello, World!"
// n = 13 (實際寫入的字符數)

4.strtok()函數

strtok?是 C 標準庫中的一個字符串分割函數,用于將字符串按指定的分隔符拆分為多個子串。該函數屬于<string.h>頭文件,常用于解析字符串數據

參數說明:

str:要分割的字符串(第一次調用時傳入,后續傳入 NULL)

delim:分隔符字符串(包含所有可能的分隔字符)

返回值:

成功:返回下一個令牌的指針

失敗/結束:返回 NULL

例子:

char str[] = "apple,banana,cherry";
char *token = strtok(str, ",");while (token != NULL) {printf("Token: %s\n", token);token = strtok(NULL, ",");
}

結果:

Token: apple
Token: banana
Token: cherry

二、頭文件和宏定義

#include <stdio.h>       // 標準輸入輸出
#include <stdlib.h>      // 標準庫函數
#include <string.h>      // 字符串處理
#include <assert.h>      // 斷言
#include <unistd.h>      // Unix標準函數
#include <sys/types.h>   // 系統類型
#include <sys/wait.h>    // 進程等待#define LEFT "["         // 提示符左括號
#define RIGHT "]"        // 提示符右括號
#define LABLE "#"        // 提示符標簽
#define DELIM " \t"      // 命令分隔符(空格和制表符)
#define LINE_SIZE 1024   // 命令行緩沖區大小
#define ARGC_SIZE 32     // 參數數組大小
#define EXIT_CODE 44     // 子進程退出碼

三、全局變量

int lastcode = 0;        // 上一條命令的退出碼
int quit = 0;            // 退出標志
extern char **environ;   // 系統環境變量
char commandline[LINE_SIZE]; // 命令行輸入緩沖區
char *argv[ARGC_SIZE];   // 參數數組
char pwd[LINE_SIZE];     // 當前工作目錄緩沖區char myenv[LINE_SIZE];   // 自定義環境變量存儲

四、核心功能函數解析

1.?信息獲取函數

const char *getusername() // 獲取用戶名
{return getenv("USER"); // 從環境變量獲取
}const char *gethostname() // 獲取主機名
{return getenv("HOSTNAME"); // 從環境變量獲取
}void getpwd() // 獲取當前工作目錄
{getcwd(pwd, sizeof(pwd)); // 系統調用獲取當前目錄
}

2.?命令行交互

void interact(char *cline, int size)
{getpwd(); // 更新當前目錄// 顯示提示符:[user@host directory]#printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);char *s = fgets(cline, size, stdin); // 讀取用戶輸入assert(s); // 確保讀取成功cline[strlen(cline)-1] = '\0'; // 去掉換行符
}

3.?命令解析

int splitstring(char cline[], char *_argv[])
{int i = 0;argv[i++] = strtok(cline, DELIM); // 分割第一個參數while(_argv[i++] = strtok(NULL, DELIM)); // 繼續分割剩余參數return i - 1; // 返回參數個數
}

4.?普通命令執行

void NormalExcute(char *_argv[])
{pid_t id = fork(); // 創建子進程if(id < 0){perror("fork");return;}else if(id == 0){// 子進程執行命令execvp(_argv[0], _argv); // 自動搜索PATHexit(EXIT_CODE); // 執行失敗退出}else{// 父進程等待子進程int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id) {lastcode = WEXITSTATUS(status); // 記錄退出碼}}
}

5.?內置命令處理(核心功能)

int buildCommand(char *_argv[], int _argc)
{// cd 命令:切換目錄if(_argc == 2 && strcmp(_argv[0], "cd") == 0){chdir(argv[1]); // 切換目錄getpwd(); // 更新當前目錄sprintf(getenv("PWD"), "%s", pwd); // 更新PWD環境變量return 1; // 表示是內置命令,不需要執行NormalExcute}// export 命令:設置環境變量else if(_argc == 2 && strcmp(_argv[0], "export") == 0){strcpy(myenv, _argv[1]); // 復制到全局變量putenv(myenv); // 設置環境變量return 1;}// echo 命令:輸出內容else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){if(strcmp(_argv[1], "$?") == 0) {// 輸出上一條命令的退出碼printf("%d\n", lastcode);lastcode = 0;}else if(*_argv[1] == '$'){// 輸出環境變量值char *val = getenv(_argv[1]+1);if(val) printf("%s\n", val);}else{// 直接輸出字符串printf("%s\n", _argv[1]);}return 1;}// 為 ls 命令自動添加顏色選項if(strcmp(_argv[0], "ls") == 0) {_argv[_argc++] = "--color"; // 添加顏色參數_argv[_argc] = NULL; // 保持數組以NULL結束}return 0; // 不是內置命令,需要執行NormalExcute
}

五、主函數流程

int main()
{while(!quit){ // 主循環// 1. 顯示提示符并獲取命令interact(commandline, sizeof(commandline));// 2. 解析命令int argc = splitstring(commandline, argv);if(argc == 0) continue; // 空命令跳過// 3. 調試輸出(注釋狀態)// for(int i = 0; argv[i]; i++) printf("[%d]: %s\n", i, argv[i]);// 4. 處理內置命令int n = buildCommand(argv, argc);// 5. 注釋中描述了管道功能的實現思路// 這里預留了管道功能的框架// 6. 執行普通命令if(!n) NormalExcute(argv);}return 0;
}

六、總代碼


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44int lastcode = 0;
int quit = 0;
extern char **environ;
char commandline[LINE_SIZE];
char *argv[ARGC_SIZE];
char pwd[LINE_SIZE];// 自定義環境變量表
char myenv[LINE_SIZE];
// 自定義本地變量表const char *getusername()
{return getenv("USER");
}const char *gethostname()
{return getenv("HOSTNAME");
}void getpwd()
{getcwd(pwd, sizeof(pwd));
}void interact(char *cline, int size)
{getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);char *s = fgets(cline, size, stdin);assert(s);(void)s;// "abcd\n\0"cline[strlen(cline)-1] = '\0';
}// ls -a -l | wc -l | head 
int splitstring(char cline[], char *_argv[])
{int i = 0;argv[i++] = strtok(cline, DELIM);while(_argv[i++] = strtok(NULL, DELIM)); // 故意寫的=return i - 1;
}void NormalExcute(char *_argv[])
{pid_t id = fork();if(id < 0){perror("fork");return;}else if(id == 0){//讓子進程執行命令//execvpe(_argv[0], _argv, environ);execvp(_argv[0], _argv);exit(EXIT_CODE);}else{int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id) {lastcode = WEXITSTATUS(status);}}
}int buildCommand(char *_argv[], int _argc)
{if(_argc == 2 && strcmp(_argv[0], "cd") == 0){chdir(argv[1]);getpwd();sprintf(getenv("PWD"), "%s", pwd);return 1;}else if(_argc == 2 && strcmp(_argv[0], "export") == 0){strcpy(myenv, _argv[1]);putenv(myenv);return 1;}else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){if(strcmp(_argv[1], "$?") == 0){printf("%d\n", lastcode);lastcode=0;}else if(*_argv[1] == '$'){char *val = getenv(_argv[1]+1);if(val) printf("%s\n", val);}else{printf("%s\n", _argv[1]);}return 1;}// 特殊處理一下lsif(strcmp(_argv[0], "ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL;}return 0;
}int main()
{while(!quit){// 1.// 2. 交互問題,獲取命令行, ls -a -l > myfile / ls -a -l >> myfile / cat < file.txtinteract(commandline, sizeof(commandline));// commandline -> "ls -a -l -n\0" -> "ls" "-a" "-l" "-n"// 3. 子串分割的問題,解析命令行int argc = splitstring(commandline, argv);if(argc == 0) continue;// 4. 指令的判斷 // debug//for(int i = 0; argv[i]; i++) printf("[%d]: %s\n", i, argv[i]);//內鍵命令,本質就是一個shell內部的一個函數int n = buildCommand(argv, argc);// ls -a -l | wc -l// 4.0 分析輸入的命令行字符串,獲取有多少個|, 命令打散多個子命令字符串// 4.1 malloc申請空間,pipe先申請多個管道// 4.2 循環創建多個子進程,每一個子進程的重定向情況。最開始. 輸出重定向, 1->指定的一個管道的寫端 // 中間:輸入輸出重定向, 0標準輸入重定向到上一個管道的讀端 1標準輸出重定向到下一個管道的寫端// 最后一個:輸入重定向,將標準輸入重定向到最后一個管道的讀端// 4.3 分別讓不同的子進程執行不同的命令--- exec* --- exec*不會影響該進程曾經打開的文件,不會影響預先設置好的管道重定向// 5. 普通命令的執行if(!n) NormalExcute(argv);}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/94534.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/94534.shtml
英文地址,請注明出處:http://en.pswp.cn/web/94534.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網關資源權限預加載:從冷啟動阻塞到優雅上線的完整閉環

網關資源權限預加載:從冷啟動阻塞到優雅上線的完整閉環 基于 Spring Cloud Gateway + Spring Cloud Alibaba Nacos ——一篇可落地的技術方案與源碼級實現 1. 場景與痛點 在微服務網關層做 統一資源權限校驗 時,必須滿足: 啟動阻塞:所有權限規則加載完成前,不監聽端口,拒…

open webui源碼分析8—管道

我們可以把Open WebUI想象成一個管道系統&#xff0c;數據通過管道和閥門流動。管道作為open webui的插件&#xff0c;可以為數據構建新的通路&#xff0c;可以自定義邏輯和處理數據&#xff1b;閥門是管道的可配置部件&#xff0c;控制數據流過管道時的行為。管道可以理解成用…

深入理解 C 語言 hsearch 哈希表:限制、技巧與替代方案

概述 C 語言標準庫中的 hsearch 系列函數提供了一套簡單易用的哈希表實現,包含在 <search.h> 頭文件中。這組函數雖然接口簡潔,但在實際使用中存在一些重要的限制和注意事項。本文將深入探討 hsearch 的功能特點、設計局限,并提供實用的解決方案和替代建議。 hsearc…

Web網絡開發 -- HTML和CSS基礎

HTML 超文本編輯語言 HTML 介紹 HTML的英文全稱是 Hyper Text Markup Language&#xff0c;即超文本標記語言。HTML是由WEB的發明者 Tim Berners-Lee &#xff08;蒂姆伯納斯李&#xff09;和同事 Daniel W. Connolly于1990年創立的一種標記語言&#xff0c; 它是標準通用化標…

Python爬蟲實戰:研究開源的高性能代理池,構建電商數據采集和分析系統

1. 緒論 1.1 研究背景與意義 隨著互聯網技術的飛速發展,網絡數據已成為信息時代的核心資源之一。從商業角度看,企業通過分析競爭對手的產品信息、用戶評價等數據,可制定更精準的市場營銷策略;從學術研究角度,研究者通過爬取社交媒體數據、學術文獻等,可開展社會網絡分析…

項目設計文檔——爬蟲項目(爬取天氣預報)

一、項目背景以及項目意義 項目背景&#xff1a; 爬蟲技術的核心目的是自動化地從互聯網上采集&#xff0c;提取和存儲數據。網絡爬蟲是一種自動化程序&#xff0c;用于從互聯網上抓取數據并進行處理。C語言因其高效性和接近硬件的特性&#xff0c;常被用于開發高性能的網絡爬…

Python 操作 PPT 文件:從新手到高手的實戰指南

在日常工作和學習中&#xff0c;PPT 是我們展示信息和進行演示的重要工具。無論是制作報告、演講還是教學課件&#xff0c;PPT 都扮演著不可或缺的角色。然而&#xff0c;當面對大量重復性的 PPT 編輯任務時&#xff0c;手動操作不僅耗時耗力&#xff0c;還容易出錯。幸運的是&…

系統設計中的冪等性

1. 基本概念 冪等性&#xff08;Idempotence&#xff09;是系統設計中經常提到的概念。如果某個操作執行一次或多次都能產生相同的結果&#xff0c;那么它就是冪等的。2. 代碼示例 下面這段代碼是冪等的。無論你調用多少次&#xff0c;show_my_button 的最終狀態都是False。 de…

Pandas vs Polars Excel 數據加載對比報告

?? Pandas vs Polars Excel 數據加載對比報告 1. 數據基本情況 數據文件:data.xlsx 數據規模:23,670 行 3 列 字段: case_time:日期/時間 case_name:公司名稱(字符串) board:所屬板塊(字符串) 2. 加載方式與代碼 Pandas import pandas as pdfrom tools import…

Kafka 為什么具有高吞吐量的特性?

Kafka 高吞吐量原因&#xff1a;面試題總結 在面試中&#xff0c;Kafka 的高吞吐量設計是高頻考點&#xff0c;核心需圍繞“架構設計”“存儲優化”“網絡效率”“資源利用”四個維度展開&#xff0c;以下是結構化總結&#xff1a; 一、核心架構&#xff1a;并行化與分層設計分…

MCP 協議原理與系統架構詳解—從 Server 配置到 Client 應用

1. MCP MCP&#xff08;Model Context Protocol&#xff0c;模型上下文協議&#xff09;是開發 Claude 模型的(Anthropic)公司推出的一個開放標準協議&#xff0c;就像是一個 “通用插頭” 或者 “USB 接口”&#xff0c;制定了統一的規范&#xff0c;不管是連接數據庫、第三方…

uniapp安卓真機調試問題解決總結

uniapp安卓真機調試遇到各種連接不上問題&#xff1a; 手機上打開調試數據線不行&#xff0c;換數據線電腦重啟手機重啟拔出數據線&#xff0c;換個USB插口。

Linux Qt創建和調用so庫的詳細教程

一、創建so庫1.文件-->新建文件或項目-->Library->C Library&#xff0c;如下圖2.工程命名為Example3.一直下一步就可以4、工程創建完成&#xff0c;如下圖5、刪除Example_global.h6、配置.pro文件# 設置輸出目錄 DESTDIR $$PWD/output #只生成.so文件 CONFIG plugi…

【深度學習】蒙特卡羅方法:原理、應用與未來趨勢

作者選擇了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰寫的《Deep Learning》(人工智能領域的經典教程&#xff0c;深度學習領域研究生必讀教材),開始深度學習領域學習&#xff0c;深入全面的理解深度學習的理論知識。 之前的文章參考下面的鏈接&#xf…

區塊鏈技術原理(18)-以太坊共識機制

文章目錄前言什么是共識&#xff1f;什么是共識機制&#xff1f;共識機制的核心目標共識機制的類型PoW&#xff08;工作量證明&#xff09;協議&#xff1a;&#xff08;2015-2022&#xff09;PoS&#xff08;權益證明&#xff09;協議&#xff1a;&#xff08;PoS&#xff0c;…

java基礎(十五)計算機網絡

網絡模型概述 為了使得多種設備能通過網絡相互通信&#xff0c;并解決各種不同設備在網絡互聯中的兼容性問題&#xff0c;國際標準化組織&#xff08;ISO&#xff09;制定了開放式系統互聯通信參考模型&#xff08;OSI模型&#xff09;。與此同時&#xff0c;TCP/IP模型作為實際…

idea將服務封裝為一個jar包

你使用的是 IntelliJ IDEA 2018&#xff0c;這個版本雖然不是最新的&#xff0c;但完全支持通過 圖形化界面 打 JAR 包&#xff08;無需命令行&#xff09;&#xff0c;非常適合你在公司內部將 Snowflake 模塊打包成通用組件。下面我將 手把手、一步一步、圖文流程式地教你&…

ZYNQ [Petalinux的運行]

一、下載ubuntu 下載地址很多&#xff0c;這里提供了一個&#xff1a;http://mirrors.aliyun.com/ubuntu-releases/14.04/ 推薦開始瀏覽器下載之后復制下載鏈接使用迅雷下載。 二、虛擬機安裝Ubuntu vmware中安裝Ubutun–這部分不展示 安裝ssh sudo apt install openssh-s…

excel 破解工作表密碼

破解Excel工作表密碼可通過易用寶工具、VBA腳本或修改文件格式實現&#xff0c;具體方法需根據文件類型和密碼保護類型選擇。 ?使用易用寶工具&#xff08;推薦&#xff09;? 適用于Excel 2007及以上版本&#xff0c;操作簡便且無需編程基礎&#xff1a; 下載安裝Excel易用…

Deepseek + RAGFlow 搭建本地知識庫問答系統

Deepseek RAGFlow 搭建本地知識庫問答系統原因為什么要本地部署RAG模型和微調模型區別本地部署流程1. 下載 ollama &#xff0c;通過ollama把Deepseek模型下載到本地運行。2. 下載RAGFlow 源代碼和 Docker &#xff0c;通過Docker部署RAGFlow。3. 在RAGFlow中構建個人知識庫并…