Linux自主實現shell

以下是在Linux操作系統 centos7版本下實現的shell ,該shell具備bash的基礎功能,無上下鍵輸入歷史命令功能,刪除字符或命令時按住Ctrl + Back

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<errno.h>
#define COMMAND_SIZE 512
#define ARGV_SIZE 32
#define SPACE " "
#define ZERO '\0'
#define NOMODE 0
#define WRITE_TRUNC 1
#define WRITE_APPEND 2
#define READ 3
#define ENDPATH(p) do{  p += strlen(p)-1; while(*p != '/')p--; }while(0)char cwd[COMMAND_SIZE * 2];
char oldcwd[COMMAND_SIZE * 2];
char* argv[ARGV_SIZE];
int errorcode = 0;
const char* filename;
const char* GetUser()  //獲取相應的環境變量內容,下同
{const char* ret = getenv("USER");if(ret == NULL)return "None";return ret;
}const char* GetHostName()
{const char* ret = getenv("HOSTNAME");if(ret == NULL)return "None";return ret;
}const char* GetHome()
{const char* ret = getenv("HOME");if(ret == NULL)return "/";return ret;
}const char* GetPwd()
{const char* ret = getenv("PWD");if(ret == NULL)return "None";return ret;
}const char* GetOldPwd()
{const char* ret = getenv("OLDPWD");if(ret == NULL)return "None";return ret;
}
void ShowUserCommand()
{char commandline[COMMAND_SIZE];  //定義一個緩沖區,用來存放命令行提示符const char* lineuser = GetUser();//獲取用戶名const char* linehostname = GetHostName(); //獲取家目錄名稱const char* linepwd = GetPwd();  //獲取當前工作路徑ENDPATH(linepwd);  //得到當前的工作路徑后,取出最后一個目錄,用宏函數實現linepwd++;linepwd = strlen(linepwd) == 0 ? "/" : (strcmp(linepwd, lineuser) == 0 ? "~" : linepwd)snprintf( commandline, COMMAND_SIZE, "[%s@%s:%s]>> ", lineuser, linehostname, line);// 如果長度為0表示當前工作路徑為根目錄,打印"/",如果當前工作路徑是家目錄,打印"~"printf("%s", commandline); //將緩沖區的內容打印到顯示器fflush(stdout);
}int SplitCommand( char* tmp)
{//將tmp中的指令切分,讀入到命令行參數表argv里int ret = NOMODE;argv[0] = strtok(tmp,SPACE);int i = 1;while((argv[i] = strtok(NULL, SPACE))){//如果指令中含有 ">>", ">","<"等與重定向相關的字符,則設置返回值ret,//對應后續相應的打開文件方式if(ret != NOMODE){i++;continue;}if(strcmp(argv[i], ">>") == 0){ret = WRITE_APPEND;filename = strtok(NULL, SPACE); //若有重定向則更新filename,為后面//的打開文件做準備,下同}else if(argv[i][0] == '>'){if(strlen(argv[i]) == 1){ret = WRITE_TRUNC;filename = strtok(NULL, SPACE);}else if(argv[i][1] == '>'){ret = WRITE_APPEND;filename = argv[i] + 2;}else {ret = WRITE_TRUNC;filename = argv[i] + 1;}}else if(argv[i][0] == '<'){ret = READ;if(strlen(argv[i]) == 1){filename = strtok(NULL, SPACE);}else {filename = argv[i] + 1;}}else {i++;}}argv[i] = NULL;return ret;
}int GetUserCommand(char* usercommand, size_t size)
{//將一整行輸入讀取到我們定義的緩沖區 usercommand中,進行后續的解析if(NULL == fgets(usercommand, size, stdin)){return 0;}usercommand[strlen(usercommand) - 1] = ZERO;return strlen(usercommand);//返回緩沖區長度,是0表示沒有讀到指令
}void Die()
{exit(1);
}
void ChangeFile(int mod)
{int fd = 0;if(mod == 0) return ;if(mod == 1 ){fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);  //打開重定向的文件,使用相應的模式打開dup2(fd,1);                                               //將文件描述符為1(標準輸出文件)的文件重定向為fd,即我們打開的文件,下同}else if(mod == 2){fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd,1);}else {fd = open(filename, O_RDONLY);dup2(fd,0);}
}
void RunUserCommand(int mod)
{pid_t id = fork();        //執行指令,為子進程運行指令的可執行程序,//父進程為shell程序,不能被影響if(id == 0)               //返回的id為0,為子進程{ChangeFile(mod);        //以mod方式打開文件execvp(argv[0], argv);  //進程替換,替換為要執行的指令exit(errno);}else if(id > 0)           //父進程{int status;if(id == waitpid(id, &status, 0) )  //父進程等待子進程,子進程結束后回收,{                                   //并且記錄退出信息statuserrorcode = WEXITSTATUS(status);  //獲取退出碼if(errorcode != 0)                //錯誤碼不為0,則子進程執行異常,打印異常信息printf("No success:%s:%d\n", strerror(errorcode), errorcode);}}else {Die();                    //創建進程失敗,退出shell}
}void Cd()
{const char* path = argv[1];  //用path記錄要進入的目錄int falg = 0;if(path == NULL ||strcmp(path, "~") == 0 ) path = GetHome();  //如果用戶沒有指定目錄,或者目錄為"~"就進入家目錄if(strcmp(path,"-") == 0 ){falg = 1;path = GetOldPwd();       //如果用戶要進入的目錄為 - ,則為最近一次進入的目錄,//環境變量oldpwd會記錄最近一次進入的目錄}char tmp[COMMAND_SIZE * 2];getcwd(tmp,sizeof(tmp)); //將當前工作路徑保存,更改路徑后用于更新oldpwdif( -1 == chdir(path))   //更改工作路徑為path{errorcode =  errno;printf("No success:%s:%d\n", strerror(errorcode), errorcode);return;}snprintf(oldcwd, sizeof(oldcwd), "OLDPWD=%s", tmp);  //在緩沖區更新環境變量OLDPWDgetcwd(tmp,sizeof(tmp));                             //獲取更改后的工作路徑,//用于更新環境變量PWDsnprintf(cwd, sizeof(cwd),  "PWD=%s", tmp);          //在緩沖區更新環境變量PWDputenv(cwd);                                         //更新環境變量PWDputenv(oldcwd);                                      //更新環境變量OLDPWDif(falg) printf("%s\n", tmp);
}int CheckBuildin(int mod)
{//是內建命令就執行,并且mod不為0時,設置相應的重定向內容int ret = 0;if(strcmp(argv[0],"cd") == 0 ){ret = 1;Cd();}else if(strcmp(argv[0],"exit") == 0 )  //用戶輸入exit就退出shell程序{ret = 1;exit(0);}else if(strcmp(argv[0], "echo") == 0 && strcmp(argv[1], "$?") == 0 ) //echo $?打印退出碼,若有重定向則更換file的指向{ret = 1;FILE* file = stdout;if(mod == 1)file = fopen(filename, "w");else if(mod == 2){file = fopen(filename, "a");}fprintf(file, "%d\n", errorcode);errorcode = 0;                      //更新退出碼if(mod != 0)fclose(file);}return ret;
}int main()
{int quit = 1;while(quit){//打印命令行提示符ShowUserCommand();//讀取命令行參數char usercommand[ARGV_SIZE * 2];int n = GetUserCommand(usercommand,sizeof(usercommand));if(n < 0) return 1;if(n == 0) continue;//切分命令行參數int mod = SplitCommand(usercommand);//判斷是否是內建命令n = CheckBuildin(mod);//執行命令if(n == 0)RunUserCommand(mod);}return 0;
}

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

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

相關文章

vue+elementUI上傳圖片至七牛云組件封裝及循環使用

1.效果&#xff08;解決循環組件賦值問題&#xff09; 廢話不多說直接上代碼 2.下載七牛云依賴 npm install qiniu-js # 或者使用 yarn yarn add qiniu-js3.在vue組件中引入 import * as qiniu from qiniu-js4.在components文件夾下創建UploadImg1/uploadImg.vue組件 <templ…

2025年6月電子學會青少年軟件編程(C語言)等級考試試卷(一級)

答案和更多內容請查看網站&#xff1a;【試卷中心 -----> 電子學會 ----> C/C ----> 一級】 網站鏈接 青少年軟件編程歷年真題模擬題實時更新 一、編程題 第 1 題 希望如光 題目描述 在充滿挑戰的生活中&#xff0c;希望往往是支撐人們穿越黑暗的核心力量。這…

拒絕復雜,AI圖表制作簡單化

在信息爆炸的時代&#xff0c;數據可視化已成為傳遞信息的核心手段。無論是職場匯報中的業績分析&#xff0c;還是學術研究里的實驗數據呈現&#xff0c;一張清晰直觀的圖表往往能勝過千言萬語。而 AI 技術的介入&#xff0c;徹底改變了圖表制作的傳統模式 —— 它不僅讓零基礎…

easypoi生成多個sheet的動態表頭的實現

在使用 EasyPOI 導出 Excel 時&#xff0c;生成多個 Sheet 且每個 Sheet 的表頭是動態的&#xff08;即每個 Sheet 的列數和列名可能不同&#xff09;&#xff0c;可以通過如下方式實現&#xff1a;? 實現原理簡述 使用 Workbook workbook ExcelExportUtil.exportExcel(expor…

移除鏈表元素+反轉鏈表+鏈表的中間節點+合并兩個有序鏈表+環形鏈表約瑟夫問題+分割鏈表

一、移除鏈表元素 給你一個鏈表的頭節點 phead 和一個整數 val &#xff0c;請你刪除鏈表中所有滿足 Node.val val 的節點&#xff0c;并返回 新的頭節點 (列表中的節點數目在范圍 [0, 104] 內) 示例&#xff1a;輸入&#xff1a;head [1,2,6,3,4,5,6], val 6 …

vue3+arcgisAPI4示例:軌跡點模擬移動(附源碼下載)

demo源碼運行環境以及配置運行環境&#xff1a;依賴Node安裝環境&#xff0c;需要安裝Node。 運行工具&#xff1a;vscode或者其他工具。 配置方式&#xff1a;下載demo源碼&#xff0c;vscode打開&#xff0c;然后順序執行以下命令&#xff1a; &#xff08;1&#xff09;下載…

Design Compiler:Milkyway庫的創建與使用

相關閱讀 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 DC Ultra推出了拓撲模式&#xff0c;在綜合時會對標準單元進行粗布局(Coarse Placement)并使用虛擬布線(Virtual Routing)技術計算互聯延遲&#xff0c;關于拓…

嵌入式教學的云端革命:高精度仿真如何重塑倒車雷達實驗與工程教育——深圳航天科技創新研究院賦能新一代虛實融合實訓平臺

一、嵌入式教學的困境與破局之道 在傳統嵌入式系統教學中&#xff0c;硬件依賴始終是核心痛點。以“倒車雷達實驗”為例&#xff0c;學生需操作STM32開發板、超聲波傳感器、蜂鳴器等硬件&#xff0c;面臨設備損耗、接線錯誤、調試效率低等問題。更關鍵的是&#xff0c;物理硬件…

flutter-boilerplate-project 學習筆記

項目地址&#xff1a; https://github.com/zubairehman/flutter_boilerplate_project/tree/master 樣板包含創建新庫或項目所需的最小實現。存儲庫代碼預加載了一些基本組件&#xff0c;例如基本應用程序架構、應用程序主題、常量和創建新項目所需的依賴項。通過使用樣板代碼…

集成電路學習:什么是CMSIS微控制器軟件接口標準

CMSIS,即Cortex Microcontroller Software Interface Standard(Cortex微控制器軟件接口標準),是由ARM公司與多家不同的芯片和軟件供應商緊密合作定義的一個標準。該標準旨在為基于ARM Cortex處理器的微控制器提供一套與供應商無關的硬件抽象層,從而簡化軟件的開發、重用,…

由淺入深使用LangGraph創建一個Agent工作流

創建一個簡單的工作流&#xff1a;Start ——> 節點1(固定輸入輸出) ——> Endfrom langchain_core.messages import SystemMessage, HumanMessage, AIMessage from langgraph.graph import StateGraph, START, END from typing_extensions import TypedDict from typing…

PL-0功能拓展及基于VSCode的IDE配置

title: PL/0功能拓展及基于VSCode的IDE配置 date: 2024-08-06 22:46:38 tags: 做過的實驗||項目復盤 top: true 概述PL/0語言可以看成PASCAL語言的子集,它的編譯程序是由C語言編寫的編譯解釋執行系統。PL/0能充分展示高級語言的最基本成分。拓展了pl0語言的基礎功能&#xff08…

【低空經濟】大型露天礦區安全生產無人機巡查與管理系統設計

1. 引言 大型露天礦區因其廣闊的作業區域和復雜的環境條件&#xff0c;安全生產管理面臨著嚴峻的挑戰。隨著科技的進步&#xff0c;無人機作為一種現代化的巡查工具&#xff0c;逐漸被應用于礦區的安全生產管理中。無人機具備高效、靈活、成本相對低廉等優點&#xff0c;可以在…

SpringCloud學習第一季-3

目錄 11.服務網關-Gateway新一代網關 一、Gateway概述 1、Gateway是什么 1.1 概述 2、 能干嘛 3、微服務架構中網關在哪里 4、為什么選擇gateway? 4.1 SpringCloud Gateway具有如下特性 4.2 SpringCloud Gateway 與 Zuul的區別 5、Zuul1.x模型 6、gateway模型 二、…

超越邊界:MongoDB 16MB 文檔限制的 pragmatic 解決方案

在軟件開發中&#xff0c;我們選擇的技術棧往往帶有一些固有的設計邊界。對于 MongoDB 而言&#xff0c;其最著名的邊界之一便是 BSON 文檔最大 16MB 的大小限制。在大多數場景下&#xff0c;這個限制是綽綽有余的&#xff0c;它鼓勵開發者設計更為精簡和規范的數據模型。然而&…

深入探討:PostgreSQL正則表達式中的郵政編碼匹配

引言 在處理大量數據時,如何高效地從字符串中提取特定模式的文本,如郵政編碼,是一個常見且具有挑戰性的任務。本文將通過一個具體實例,探討在PostgreSQL中使用正則表達式匹配加拿大郵政編碼的問題,并提供解決方案。 問題描述 我們希望能夠從字符串中提取所有符合加拿大…

集合框架(重點)

第十五天集合框架1.什么是集合 Collections集合Collection&#xff0c;也是一個數據容器&#xff0c;類似于數組&#xff0c;但是和數組是不一樣的。集合是一個可變的容器&#xff0c;可以隨時向集合中添加元素&#xff0c;也可以隨時從集合中刪除元素。另外&#xff0c;集合還…

深度學習核心:神經網絡-激活函數 - 原理、實現及在醫學影像領域的應用

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家、CSDN平臺優質創作者&#xff0c;高級開發工程師&#xff0c;數學專業&#xff0c;10年以上C/C, C#,Java等多種編程語言開發經驗&#xff0c;擁有高級工程師證書&#xff1b;擅長C/C、C#等開發語言&#xff0c;熟悉Java常用開發…

OneCode3.0 核心表達式技術深度剖析:從架構設計到動態擴展

一、引言&#xff1a;表達式技術在企業級框架中的核心價值 在當今快速變化的企業級應用開發中&#xff0c;動態性和靈活性已成為衡量框架優劣的關鍵指標。OneCode 3.0 框架作為企業級應用開發的重要工具&#xff0c;其核心表達式技術提供了一種強大的解決方案&#xff0c;使開發…

[css]旋轉流光效果

實現一個矩形的旋轉流光邊框效果。 需要使用css屬性梯度漸變&#xff1a;鏈接: conic-gradient&#xff0c;他指的是圓錐形變化的梯度。 // html<div class"demo"></div>// css body {width: 100%;height: 100%;background-color: black; }.demo {width…