教你寫一個簡單的myshell
一.myshell的功能:
1. ls
2. 輸入輸出重定向
3. 管道操作
4. linux的內建命令 cd
5. 簡單的history
6. 遮蔽ctrl + c
二.主要難點:
1.命令解析
2.程序的應用
3.cd命令的輸出格式
三.程式碼:
#include<stdio.h>
# include <sys/types.h>
# include <unistd.h>
# include <sys/wait.h>
# include <stdlib.h>
# include <string.h>
# include <dirent.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <linux/limits.h>
# include <errno.h>
# include <signal.h>
# define P 3 // 管道
# define I 2 // 輸入重定向
# define V 4 // 兩個>>輸出流重定向
# define O 1 // 輸出重定向
# define N 0 // 一般的命令
void output( char argv[][256 ], int sum );
void analyse(int , char fen[][256]);//執行引數
void fenge( char *buf, char fen[][256], int *len );
int find_mingli(char *canshu[256]);//一次只傳進來一個,所以用一維的
void my_error( const char * err_string, int line )
{
fprintf(stderr,"line : %d\n", line);
perror( err_string );
exit(-1);
}
void my_cd(char fen[][256], char str[256], int len)
{
char buf[256]="/home/zongjin/";
char *arg2[256];
int i;
for( i = 0; i<len; i++ )
{
arg2[i] = (char*)fen[i];
arg2[i][strlen(arg2)] = '\0';
}
arg2[i] = NULL;
if(arg2[1] == NULL)
{
chdir(buf);
strcpy(str,buf);
str[strlen(str)]='\0';
}
if( arg2[1] != NULL )
{
chdir(arg2[1]);
strcpy(str,arg2[1] );
str[strlen(str)]='\0';
}
}
void xinhao(int signo, sigset_t * newmask, sigset_t * oldmask)
{
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
}
sigset_t newmask, oldmask;
int main(void)
{
int i=0, len,j;
char buf[256];
char argv[100][256];
char fen[100][256];
char str[256]= "mashell:";
int fd;
//遮蔽ctrl - c
sigemptyset(&newmask);//建立一個訊號箱
sigaddset(&newmask, SIGINT);//將ctrl+c的訊號放到訊號箱內
while(1)
{
len =0;
printf("%s:",str);
signal(SIGINT, xinhao);//呼叫函式xinhao
gets(argv[i]);
strcpy(buf, argv[i]);
buf[strlen(buf)] = '\0';
if( (strcmp(buf, "exit") ==0 ) || (strcmp(buf, "logout" ) == 0))
{
break;
}
i++;
if( strcmp( buf, "history" ) == 0 )
{
output(argv, i);
}
fenge(buf, fen, &len);
if( strcmp( fen[0], "cd" ) == 0 )
{
strcpy( str, "mashell:");
char str2[256];
my_cd(fen, str2,len);
strcat( str,str2 );
str[strlen(str)]='\0';
str2[256]=NULL;
continue;
}
analyse(len, fen);
}
exit(0);
}
void output( char argv[100][256], int sum )
{
int i;
char buf[256];
for( i = 0; i<sum; i++ )
{
printf("%d:%s\n",i+1,argv[i]);
}
}
//命令解析,以空格進行分割
void fenge( char * buf, char fen[100][256], int * len )
{
int i, j=0;
char p[256];//這塊不能寫成char *p的形式,否則出現段錯誤
for( i = 0; buf[i]!= NULL; i++ )
{
p[j] = buf[i];
j++;
if( buf[i] == ' ')
{
strcpy(fen[*len], p );
fen[*len][j-1]= '\0';
j=0;
*len = *len +1;
}
if( buf[i+1] == '\0' )
{
strcpy(fen[*len], p);
fen[*len][j]= '\0';
*len= *len +1;// 這塊不能寫成*len++這樣len的值不變
j=0;
}
}
}
//分析命令
void analyse(int len, char fen[100][256])
{
int i,fd, status, pid2,fd2,ccc;
int t=0;//收納各種符號
int sum;//以空格記錄命令的個數
int hou;//單個記錄後臺符的個數,是一個輔助命令,所以單個記錄下來
int flag=0;//記錄各種輸入的各種符號的個數
pid_t pid;
pid_t child_pid;
char *file;
char *arg[256];
char *str = "cd";
char* argnext[len+1];
sum = len;
hou = 0;
for( i = 0; i<sum; i++ )
{
arg[i] = (char*)fen[i];
arg[i][strlen(arg[i])] = '\0';
}
arg[i] = NULL;
for( i = 0; i< sum; i++ )
{
if( strcmp(arg[i], "&") ==0 )
{
if( i ==sum-1 )
{
hou = 1;
arg[i] == NULL;
}
else
{
printf("輸入錯誤,後臺符必須放到最後!\n");
exit(-1);
}
}
}
//判斷有沒有輸出重定向符號'>'並且必須讓符號的後面必須要有檔名
for( i = 0; arg[i]!=NULL; i++ )
{
if( strcmp( arg[i], ">" ) == 0 && arg[i+1] != NULL)
{
flag +=1;
if( flag ==1 )
{
t =O;
file = arg[i+1];
arg[i] =NULL;
}
puts(file);
if( flag>1 )
{
printf("輸入錯誤\n");
return ;
}
}
//判斷有沒有輸入重定向'<'並且必須讓這個符號前面有東西
if(arg[i] != NULL && strcmp( arg[i], "<" ) == 0 && i!=0)
{
flag +=1;
if( flag ==1 )
{
t =I;
file = arg[i+1];
arg[i] =NULL;
}
else
{
printf("輸入錯誤\n");
return ;
}
}
//命令只有一個管道'|' 並且它的前後必須都要有東西,還要將它後面的命令記錄下來
if ( arg[i] != NULL && strcmp( arg[i], "|" ) == 0 && i!=0 && arg[i+1] != NULL )
{
arg[i] = NULL;
flag +=1;
if(flag ==1)
{
t = P;
int j;
for( j = i+1; arg[j] != NULL; j++ )
{
argnext[j-i-1] = arg[j];
}
argnext[j-i-1] = arg[j];
break;
}
else
{
printf("輸入錯誤!\n");
return;
}
}
}
if( ( pid = fork() ) < 0 )
{
printf("fork error\n");
return;
}
switch(t)
{
case 0:
if(pid == 0)
{
if(!( find_mingli(arg)))
{
printf("系統目錄下沒有%s這個命令\n",arg[0]);
exit(0);
}
if( execvp(arg[0],arg) ==-1)
{
perror("execvp");
}
exit(0);
}
break;
case 1://輸出流重定向
if( pid == 0 )
{
if( !( find_mingli(arg) ) )
{
printf("沒有這個%s命令\n", arg[0]);
exit(0);
}
fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0644);
if( fd < 0 )
{
my_error("open", __LINE__);
}
dup2( fd, 1 );
execvp(arg[0], arg);
exit(0);
}
break;
case 2://輸入流重定向
if( pid == 0 )
{
if( !find_mingli( arg ) )
{
printf("沒有這個命令%s\n", arg[0]);
exit(0);
}
if( (fd = open( file, O_RDONLY )) < 0 )
{
my_error("open", __LINE__);
}
dup2( fd, 0 );
execvp(arg[0], arg);
exit(0);
}
break;
case 3://在子程序中先執行管道符前面的,在父程序中執行後面的,父程序等待子程序結束
if( pid == 0 )
{
if( (pid2 = fork()) <0 )
{
printf("make process erron\n");
exit(-1);
}
if( pid2==0 )//在子程序中先執行管道符前面的
{
if( !find_mingli(arg) )
{
printf("沒有這個命令%s\n",arg[0]);
exit(-1);
}
if( (fd2 = open("/tmp/xiaoming", O_RDWR|O_CREAT|O_TRUNC, 0644)) <0 )
{
my_error("open", __LINE__);
exit(-1);
}
dup2( fd2, 1 );//將父程序檔案的檔案描述符複製給新的FD2,這樣fd2中就包含了父程序的所有資訊
execvp( arg[0], arg );//在環境變數中查詢可執行的程式,執行引數arg中的命令
exit(0);//關閉程序,他有一個返回值,在8到15之間,這個值放到核心中等待父程序通過wait(&reslt)來取這個值
}
if( waitpid(pid2, &ccc, 0 ) <0 )//父程序等待子程序結束
{
printf("等待失敗!\n");
}
if( !find_mingli(argnext) )
{
printf("沒有這個命令%s\n", argnext[0]);
exit(-1);
}
fd2 = open("/tmp/xiaoming", O_RDONLY);//開啟子程序的那個檔案,輸入到當前的程式中,執行新的引數
dup2(fd2, 0);
execvp(argnext[0], argnext);
if( remove( "/tmp/xiaoming" ) )//正確移除返回0, 沒有移除返回-1
{
printf("remove error\n");
}
exit(0);
}
break;
default :
break;
}
if( hou == 1 )
{
return ;
}
if( waitpid( pid, &status, 0) == -1 )
{
printf(" 等待錯誤 !\n");
}
}//這個函式寫完,有引數 t , arg , file ,
int find_mingli(char *arg[256])//這個函式的作用是判斷你輸入的命令在系統的命令目錄裡只否有,有則返回1,無則返回0
{
DIR *dir;
struct dirent *ptr;
int i=0;
char *path[] = {"./", "/bin", "/usr/bin", NULL};
//是當前目錄下的程式可以正常執行
if( strncmp( arg[0], "./", 2 ) == 0 )
{
arg[0] = arg[0] +2;//暫時不知道在什麼情況下用
}
while(path[i]!= NULL)
{
if( ( dir = opendir(path[i]) ) <0 )
{
printf("沒有這個引數 /bin\n");
}
while( (ptr = readdir(dir) ) != NULL )
{
if( strcmp( ptr->d_name , arg[0]) == 0 )
{
closedir( dir);
return 1;
}
}
closedir(dir);
i++;
}
return 0;
}
相關推薦
教你寫一個簡單的myshell
一.myshell的功能: 1. ls 2. 輸入輸出重定向 3. 管道操作 4. linux的內建命令 cd 5. 簡單的history 6. 遮蔽ctrl + c 二.主要難點: 1.命令解析 2.程序的應用 3.
教你寫一個簡單的網頁(html網頁開發入門)
網頁的組成 HTML 網頁的具體內容和結構 CSS 網頁的樣式(美化網頁最重要的一塊) JavaScript 網頁的互動效果,比如對使用者滑鼠事件作出響應 HTML 什麼是HTML HTML的全稱是HyperTextMarkupLanguage,超文字標
手把手教你編寫一個簡單的PHP模塊形態的後門
cpp rest xtu job ring 事先 們的 original call 看到Freebuf 小編發表的用這個隱藏於PHP模塊中的rootkit,就能持久接管服務器文章,很感興趣,苦無作者沒留下PoC,自己研究一番,有了此文 0×00. 引言 PHP是一個非常流行
大神手把手教你寫一個頁面模板引擎,只需20行Javascript代碼!
[1] 表達 最終 strong ice ali 開頭 syntax years 只用20行Javascript代碼就寫出一個頁面模板引擎的大神是AbsurdJS的作者,下面是他分享的全文,轉需。 不知道你有木有聽說過一個基於Javascript的Web頁面預處理器,叫做A
教你寫一個含信息的匯編程序
size net align ads data- str 匯編代碼 data tps dosbox官網: https://www.dosbox.com/ https://download.csdn.net/download/fouken_ma/7682497 其他工具下
Istio技術與實踐04:最佳實踐之教你寫一個完整的Mixer Adapter
Istio內建的部分介面卡以及相應功能舉例如下: circonus:微服務監控分析平臺。 cloudwatch:針對AWS雲資源監控的工具。 fluentd:開源的日誌採集工具。 prometheus:開源的時序資料庫,非常適合用來儲存監控指標資料。 statsd:採
現要求你寫一個簡單的員工資訊增刪改查程式.
1,Alex Li,22,13651054608,IT,2013‐04‐01 2,Jack Wang,28,13451024608,HR,2015‐01‐07 3,Rain Wang,21,13451054608,IT,2017‐04‐01 4,MacQiao,44,1
微信小程式——手把手教你寫一個微信小程式
前言 微信小程式年前的跳一跳確實是火了一把,然後呢一直沒有時間去實踐專案,一直想搞但是工作上不需要所以,嗯嗯嗯嗯嗯emmmmm..... 需求 小程式語音識別,全景圖片觀看,登入授權,獲取個人基本資訊 一:基礎框架 跟著官方文件一步一步來,新建一個小程式專案就好 然後呢,畢竟預設的只是基本骨架
手把手教你寫一個手勢密碼解鎖View(GesturePasswordView)
相信大家在很多的app肯定看到過手勢密碼解鎖View,但是大家有沒有想過怎麼實現這樣一個View,哈,接下來,小編手把手教大家教寫一個GesturePasswordView。 先看一張效果圖 要實現這樣一個效果,首先需要在螢幕上繪製一個3x3九宮圖,如下圖 具體思路:
教你搭一個簡單的前後端互動小網站
最近想自己搭一個網站,但僅僅會前端還是有點做不到實際情況中的前後端互動,就自己瞎鼓搗了幾個工具,來搭一個簡單的網站。 工具: 後端處理這種自己做的小網站用 Node是最好不過來(其他語言不會。。),選Express跟Koa都可以,這邊我選的是Express
手把手教你寫一個基於RxJava的擴充套件框架
今日科技快訊近日訊息,由於央行不再新設第三方支付機構,網際網路企業們只能買買買。最近有通知稱,中
一步一步教你寫一個快遞查詢APP(適合新手)
前言: 水平:自學Android十五天,以前有過混日子的程式設計經驗。 目標: 《第一行程式碼》學完之後,總想寫個APP,天氣的APP寫了個初版,後面再說,今天演示的是製作快遞查詢APP的整個經過。 適合人群:新手 工具:A
前端實戰:教你寫出簡單的側邊欄功能以及返回頂部特效
個人部落格網站文章地址:http://blog.mclink.xyz/index/article/index/id/33.html 前陣子博主忙著一些事情,有些時間沒更新了,幾天前有個側邊欄的需求,於是自己簡單用幾個框架簡單實現了一個。原理挺簡單的,在此記錄一下。先放效
教你寫一個炫酷的Material Design 風格的登入和註冊頁面
每個人都會喜歡漂亮的登入介面,一個App 給人們的第一印象是非常重要的。 這篇文章將教你使用谷歌材料設計規範(Material design spec )和谷歌的新的設計支援庫( design support library)來建立一個炫酷的登入和註冊介面。設
從完全零基礎開始教你寫一個Python機器人!每天唯一秒回你的人!
註釋:全面教程,入門書籍,學習原始碼可以新增小編學習群943752371直接獲取。 提供航班資訊 連線客戶和他們的財務 作為客戶支援 可能性(幾乎)是無限的。 聊天機器人的歷史可以追溯到1966年,當時韋森鮑姆發明了一種名為“伊麗莎”(ELIZA)的電腦程
每個人都能徒手寫遞迴神經網路–手把手教你寫一個RNN
總結: 我總是從迷你程式中學到很多。這個教程用python寫了一個很簡單迷你程式講解遞迴神經網路。 遞迴神經網路即RNN和一般神經網路有什麼不同?出門左轉我們一篇部落格已經講過了傳統的神經網路不能夠基於前面的已分類場景來推斷接下來的場景分類,但是RNN確有一定記
後天晚上,手把手教你寫一個全球辨識度最高的遊戲!
這個遊戲曾8次創造吉尼斯世界紀錄是全球辨識度最高的遊戲三款被華盛頓國家檔案館收藏遊戲之一多次被創
教你寫一個ftp協議(檔案傳輸協議)
一、FTP協議簡介 FTP 是File Transfer Protocol(檔案傳輸協議)的英文簡稱,而中文簡稱為“文傳協議”。用於Internet上的控制檔案的雙向傳輸。同時,它也是一個應用程式(Application)。基於不同的作業系統有不同的FTP應用
手把手教你寫一個完整的自定義View
前言 自定義View是Android開發者必須瞭解的基礎 今天,我將手把手教你寫一個自定義View,並理清自定義View所有應該的注意點 目錄 1. 自定義View的分類 自定義View一共分為兩大類,具體如下圖: 2.
只有20行Javascript程式碼!手把手教你寫一個頁面模板引擎
AbsurdJS 作者寫的一篇教程,一步步教你怎樣用 Javascript 實現一個純客戶端的模板引擎。整個引擎實現只有不到 20 行程式碼。如果你能從頭看到尾的話,還能有不少收穫的。你甚至可以跟隨大牛的腳步也自己動手寫一個引擎。以下是全文。 不知道你有木有聽說過一個基