模擬Linux的shell

来源:http://www.cnblogs.com/lenomirei/archive/2016/06/25/5616797.html
-Advertisement-
Play Games

在學習了Linux的進程式控制制之後,學習了fork函數和exec函數族,通過這些個函數可以簡單的實現一份shell,就是實現一份命令行解釋器,當然是簡單版的,實現功能如下 還不能實現正則表達式,要實現這個我當前的代碼根本不能用,要重頭開始改寫。。。 下麵貼代碼 通過gethostname獲取主機名,通 ...


在學習了Linux的進程式控制制之後,學習了fork函數和exec函數族,通過這些個函數可以簡單的實現一份shell,就是實現一份命令行解釋器,當然是簡單版的,實現功能如下

  1. 能執行普通的命令如ls ,ps ,top等
  2. 可以實現目錄的跳轉cd命令
  3. 能執行命令並加上參數如ls-l
  4. 能執行打開man手冊
  5. 能識別管道符

還不能實現正則表達式,要實現這個我當前的代碼根本不能用,要重頭開始改寫。。。

下麵貼代碼

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 #include <stdlib.h>
  7 #include <pwd.h>
  8 #include <sys/utsname.h>
  9 #include <libgen.h>
 10 
 11 
 12 void eatblank(char **buf)
 13 {
 14   while(**buf==' ')
 15   {
 16     (*buf)++;
 17   }
 18 }
 19 
 20 
 21 void GetHostName(char *hostname,int length)
 22 {
 23   gethostname(hostname,length);
 24   char *p=hostname;
 25   while(*p!='\0')
 26   {
 27     if(*p=='.')
 28     {
 29       *p='\0';
 30     }
 31     p++;
 32   }
 33 }
 34 
 35 void Pipe(char **my_argv,char *buf);
 36 void BuildCommand(char **my_argv,char *buf)
 37 {
 38   eatblank(&buf);
 39   my_argv[0]=buf;
 40   int index=1;
 41   char *p=buf;
 42       while(*p!='\0')
 43       {
 44         if(*p==' ')
 45         {
 46           *p='\0';
 47           p++;
 48           eatblank(&p) ;
 49           if(*p!='|')
 50           {
 51             my_argv[index++]=p;
 52           }
 53           continue;
 54         }
 55         else if(*p=='|')
 56         {
 57           p++;
 58           //p++;
 59           my_argv[index]=NULL;
 60           Pipe(my_argv,p);
 61         }
 62         else
 63         {
 64           p++;
 65         }
 66       }
 67      my_argv[index]=NULL;
 68 }
 69 
 70 
 71 
 72 void Pipe(char ** my_argv,char *buf)
 73 {
 74   int fd[2];
 75   pipe(fd);
 76   pid_t id2=fork();
 77   if(id2==0)
 78   {
 79     close(1);
 80     dup(fd[1]);
 81     close(fd[1]);
 82     close(fd[0]);
 83     execvp(my_argv[0],my_argv);
 84   }
 85   else
 86   {
 87     waitpid(id2,NULL,0);
 88     close(0);
 89     dup(fd[0]);
 90     close(fd[0]);
 91     close(fd[1]);
 92     BuildCommand(my_argv,buf);
 93     execvp(my_argv[0],my_argv);
 94   }
 95   //在此處添加exec族函數
 96 }
 97 
 98 
 99 int main()
100 {
101   while(1)
102   {
103     char *my_argv[64];
104       struct passwd *pwd=getpwuid(getuid());
105       char hostname[256]={'\0'};
106       char cwd[256]={'\0'};
107       getcwd(cwd,256);
108       GetHostName(hostname,256);
109       printf("[%s@%s %s]#",pwd->pw_name,hostname,basename(cwd));
110       fflush(stdout);
111       char buf[1024];
112       buf[0]='\0';
113 
114       int count=read(0,buf,sizeof(buf));
115       buf[count-1]='\0';
116       my_argv[0]=buf;    
117     pid_t id=fork();
118     if(id==0)
119     {
120       //child
121      
122       if(strncmp(buf,"cd",2)==0) 
123       {
124         exit(1);
125       }
126       BuildCommand(my_argv,buf);
127      execvp(my_argv[0],my_argv);
128      printf("if the process has some problem ,I should run here\n");
129      exit(0);
130     }
131     else
132     {
133       //father
134       int status=0;
135       wait(&status);
136       if(status==256)
137       {
138         my_argv[0]+=3;
139         chdir(my_argv[0]);
140       }
141     }
142   }
143   return 0;
144 }

通過gethostname獲取主機名,通過getcwd獲得當前工作目錄,通過getpwuid獲得當前登錄的用戶信息

這樣命令提示符就完成了;

  • 普通命令的實現

普通命令的實現並不困難,我的目的是讓子進程來執行命令,也就是通常的讓fork產生的子進程執行exec族函數,然後自己死掉,通過父進程回收資源,迴圈並創建新的子進程,這就是shell的大框架,其中用execvp函數來實現命令的執行,函數原型就不多說了,查手冊就能查到,簡單解釋一下參數,第一個參數是命令行的字元串,第二是參數是一個字元串數組,從上到下依次存放,命令,參數(可能有多個,一個參數占一個下標),最後用NULL占據一個下標表示結束。

  • cd命令的實現

cd命令的實現有些問題,不是普通命令的實現,就是說簡單的使用execvp是不能實現的,因為就運算元進程改變了目錄之後也會把自己殺死,父進程和子進程之間是不通的(就是說父進程和子進程並不共用環境變數,子進程修改了當前工作目錄的環境變數對父進程也沒有什麼影響),後來使用system來執行系統調用,也失敗,因為更多時候shell會產生一個子進程來執行命令,因為shell本身執行會有風險,可能會因為用戶的錯誤操作把自己掛掉,所以使用子進程來執行命令,而這樣和剛纔的結果是一樣的並不會影響到父進程,最後採用了chdir函數,他可以改變當前進程的環境變數中的工作目錄(就是專門change dir用的),讓父進程來執行,fork出來的子進程會有一份父進程環境變數的拷貝,這就完成了改變目錄,並將結果傳遞了下來

 1  else
 2     {
 3       //father
 4       int status=0;
 5       wait(&status);
 6       if(status==256)
 7       {
 8         my_argv[0]+=3;
 9         chdir(my_argv[0]);
10       }
11     }

 

  • 管道符的實現

管道符的實現很簡單,平常我們執行命令的時候,都是結果自動輸出到電腦屏幕上,這說明一般命令的輸出是write在標準輸出stdout的,而我們輸出命令的參數是通過鍵盤,這說明命令的輸入來源是標準輸入stdin,如果我們關閉了,標準輸出和標準輸入,而是通過一個管道,讓結果寫進管道,然後讓參數從管道中讀取(簡單的說就是讓管道的兩段代替標準輸入和標準輸出),管道符就實現了

 1 void Pipe(char ** my_argv,char *buf)
 2 {
 3   int fd[2];
 4   pipe(fd);
 5   pid_t id2=fork();
 6   if(id2==0)
 7   {
 8     close(1);
 9     dup(fd[1]);
10     close(fd[1]);
11     close(fd[0]);
12     execvp(my_argv[0],my_argv);
13   }
14   else
15   {
16     waitpid(id2,NULL,0);
17     close(0);
18     dup(fd[0]);
19     close(fd[0]);
20     close(fd[1]);
21     BuildCommand(my_argv,buf);
22     execvp(my_argv[0],my_argv);
23   }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 【資料】 http://www.ibm.com/developerworks/cn/views/linux/libraryview.jsp http://www.kerneltravel.net/ 【代碼】 https://cdn.kernel.org/pub/linux/kernel/v4.x/l ...
  • 前言 看了很久的操作系統原理,ucos源碼也看了大半,但是感覺總是懵懵懂懂,用句流行的網路用語就是始終上不了車,後來在網上被人推薦了一篇文章《建立一個屬於自己的操作系統》,這篇文章真的非常好,也附有源碼,但不知道是不是我找的文章有差錯還是啥,我根據文章提供的源碼貼代碼,根本無法編譯,然後開始讀代碼修 ...
  • 一. 命令: .tar 解包:tar xvf FileName.tar打包:tar cvf FileName.tar DirName(註:tar是打包,不是壓縮!)———————————————.gz解壓1:gunzip FileName.gz解壓2:gzip -d FileName.gz壓縮:gz ...
  • l vi編輯器開發步驟 A)輸入 vi Hello.java B) 輸入 i 插入模式。 C)輸入 冒號。【保存退出:wq】【退出不保存:q!】 l 列出當前目錄的所有文件:ls 詳細信息的列表:ls -l l 1.編譯c程式:gcc(自動生成a.out) 2.輸入 ./a.out 運行c++程式。 ...
  • 一、查看官方提供的下載源 https://docs.puppet.com/guides/puppetlabs_package_repositories.html 二、 選擇對應系統的下載源 因為本機是CentOS 7.1,故選擇YUM源 https://yum.puppetlabs.com/ 三、 ...
  • 配置openswan ipsecvpn Tags: Linux [TOC] 實驗環境: OS: VPC1: VPC2: 安裝openswan 編輯 文件,啟用 在/etc/ipsec.d 目錄創建以下文件 配置VPC1 配置VPC2 啟動 IPSec/Openswan 編輯/etc/sysctl.c ...
  • 工作的一些內容,這是中國移動集團當前linux機器安全合規標準,找了點時間將其歸類,並查了一些資料,每項配置是什麼意思,不僅要知其然,還要知其所以然。好記性不如爛筆頭。 1. 檢查FTP配置-限制用戶FTP登錄 控制FTP進程預設訪問許可權,當通過FTP服務創建新文件或目錄時應屏蔽掉新文件或目錄不應有 ...
  • 一. Linux特點 1.免費/開源; 2.支持多線程/多用戶; 3.安全性好; 4.對記憶體和文件管理優越。 Linux最小隻需4M ——> 嵌入式開發 二. 文件目錄 Linux系統所有軟硬體都是以文件的形式存在,可以自由設置、掛載、卸載。瞭解Linux文件目錄,是學習Linux的關鍵。 主要目錄 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...