一、什麼是shell
在計算機科學中,Shell俗稱殼(用來區別於核),是指“爲使用者提供操作界面”的軟件(命令解析器)。它類似於DOS下的command.com和後來的cmd.exe。它接收用戶命令,然後調用相應的應用程序。
——《百度百科》
shell命令編輯器更多地用於unix/linux系統之中。
二、節選程序
int main()
{
/****************************聲明程序中用到的函數*****************************/
int redirect(); /*重定向命令的處理函數*/
int pipel(); /*管道命令的處理函數*/
int get_line(); /*讀取一行的函數*/
int is_founded(); /*查找命令的函數*/
void init_environ(); /*初始化環境變量的函數*/
void getenviron(); /*初始化查找路徑的函數*/
void add_history(const char *string);/*調用上下方向鍵實現history命令的函數*/
void add_his_link(); /*記錄history命令的函數*/
void history_cmd(); /*顯示history命令的函數*/
void cd_cmd(); /*處理cd命令的函數*/
void jobs_cmd(); /*處理jobs命令的函數*/
void add_node(); /*向jobs命令的鏈表中增加節點函數*/
void del_node(); /*向jobs命令的鏈表中刪除節點函數*/
void ctrl_z(); /*處理用戶按下ctrl_z時的函數*/
void setflag(); /*將標誌位置一的函數*/
void bg_cmd(); /*處理bg命令的函數*/
void fg_cmd(); /*處理fg命令的函數*/
init_environ(); /*初始化環境變量,將查找路徑至於envpath[]中,
//初始化history,和jobs的頭尾指針*/
while (1)
{
char c,*arg[20];
char hostName[60],path_string[40];
int i=0,j=0,k=0,is_pr=0,is_bg=0,input_len=0,path,pid=0,status=0,is_addpre=0,is_exe=0,is_absolute=0;
/**************************** 設置signal信號 *****************************/
struct sigaction action;//sigaction()函數用於改變進程接收到特定信號後的行爲
action.sa_sigaction=del_node;//指向“新式”信號處理函數的指針
sigfillset(&action.sa_mask);//sa_mask 用來設置在處理該信號時暫時將sa_mask 指定的信號集擱置
action.sa_flags=SA_SIGINFO;//sa_flags置爲SA_SIGINFO(使用新式
sigaction(SIGCHLD,&action,NULL);
signal(SIGTSTP,ctrl_z);
/**************************** 打印提示符 *****************************/
path=get_current_dir_name();//獲得當前路徑名
gethostname(hostName,sizeof(hostName));//gethostname() : 返回本地主機的標準主機名
sprintf(path_string,"%s",path); //path路徑
strcat(hostName,"@");
strcat(hostName,path_string);
strcat(hostName,"#"); //近似構成linux下的shell提示符
char * p =readline(hostName); //使用readline實現tab補全
if(strlen(p)==0)
continue;//當字符串p的長度爲0時,即用戶只是按下回車鍵,並沒有輸入任何字符,因此不需要對後面的操作進行處理
add_history(p); //調用<readline/history.h>頭文件中的add_history函數實現上下方向鍵的history命令
/**************************** 獲取用戶輸入 *****************************/
i=0;
while(p[i]==' ' || p[i]=='\t'|| p[i]==EOF)
i++; //跳過空格等無用信息
for(;i<strlen(p);i++){
if(p[i]=='\n')
continue; //輸入爲空時結束本次循環打印提示符
else {
buf[input_len++]=p[i];
}
}
buf[input_len]='\0'; //加上串結束符
if(buf[0]=='.'&&buf[1]=='/') //判斷是否爲./命令
is_exe=1;
if(buf[0]=='/')
is_absolute=1;
/*分配動態存儲空間,將命令從緩存拷貝到input中*/
input=(char *) malloc(sizeof(char)*(input_len+1));
strcpy(input,buf);
add_his_link(input);
/**************************** 解析指令 *****************************/
/******************管道和重定向命令單獨處理**************/
for (i=0,j=0,k=0;i<=input_len;i++){
if (input[i]=='<' || input[i]=='>' ||input[i]=='|'){
if (input[i]=='|'){
pipel(input,input_len);/*管道命令*/
//add_history(input);
free(input);
}
else{
//printf("is_addpre=%d,input[%d]=%c\n",is_addpre,i+1,input[i+1]);
if (input[i]=='>' && input[i+1]=='>')
is_addpre=1;
redirect(input,input_len,is_addpre);/*重定向命令*/
//add_history(input);
free(input);
}
is_pr=1;
break;
}
}
/********************** 普通命令 ***********************/
if (is_pr==1) continue;
if(is_addpre==1) continue;
for (i=0,j=0,k=0;i<=input_len;i++){
if (input[i]==' ' || input[i]=='\0'){
if (j==0) /*這個條件略去連在一起的多個空格*/
continue;
else{
buf[j++]='\0';
arg[k]=(char *) malloc(sizeof(char)*j);
strcpy(arg[k++],buf);/*將指令或參數拷到arg中*/
// printf("arg[%d]=%s\n",k-1,arg[k-1]);
j=0; /*準備取下一參數*/
}
}
else{
/*如果字符串最後是“&”,將後臺命令標誌置一*/
if (input[i]=='&' && input[i+1]=='\0'){
is_bg=1;
continue;
}
buf[j++]=input[i];
}
}
/********************** 內部命令的處理 ********************/
/*exit命令,退出*/
if (strcmp(arg[0],"exit")==0) {
//add_history(input);
printf("Shell exit.\n");
free(input);
break;
}
/*history命令,顯示history數組中保存的歷史命令*/
if (strcmp(arg[0],"history")==0) {
//add_history(input);
history_cmd();
free(input);
continue;
}
/*cd命令,改變當前路徑*/
if (strcmp(arg[0],"cd")==0) {
//add_history(input);
for (i=3,j=0;i<=input_len;i++)/*獲取路徑*/
buf[j++]=input[i];
buf[j]='\0';
arg[1]=(char *) malloc(sizeof(char)*j);
strcpy(arg[1],buf);/*將路徑保存到arg[1]中*/
cd_cmd(arg[1]);/*cd_cmd()函數,改變路徑到指定路徑*/
free(input);
continue;
}
/*jobs命令,顯示現有工作*/
if (strcmp(arg[0],"jobs")==0) {
//add_history(input);
jobs_cmd();/*jobs_cmd()函數,遍歷鏈表,顯示所有工作*/
free(input);
continue;
}
/*bg命令,將作業放到後臺執行*/
if (strcmp(arg[0],"bg")==0) {
//add_history(input);
/*獲取制定的作業號,作業號在%後*/
for (i=0;i<=input_len;i++) {
if (input[i]=='%')
break;
}
i++;
for (;i<=input_len;i++)
buf[j++]=input[i];
buf[j]='\0';
arg[1]=(char *) malloc(sizeof(char)*j);
strcpy(arg[1],buf);/*將作業號保存在arg[1]中*/
// printf("arg[1]=%s\n",arg[1]);
bg_cmd(atoi(arg[1]));/*bg_cmd命令,將指定作業放到後臺運行*/
free(input);
continue;
}
/*fg命令,將作業放到前臺執行*/
if (strcmp(arg[0],"fg")==0) {
//add_history(input);
/*獲取制定的作業號,作業號在%後*/
for (i=0;i<=input_len;i++) {
if (input[i]=='%')
break;
}
i++;
for (;i<=input_len;i++)
buf[j++]=input[i];
buf[j]='\0';
arg[1]=(char *) malloc(sizeof(char)*j);
strcpy(arg[1],buf);/*將作業號保存在arg[1]中*/
printf("%s\n",arg[1]);
fg_cmd(atoi(arg[1]));/*fg_cmd命令,將指定作業放到後臺運行*/
free(input);
continue;
}
/**************************** 尋找命令文件 *****************************/
if (is_pr==0 ){/*非管道、重定向命令、非./、非絕對路徑*/
/*在使用exec執行命令時,最後的參數必須是NULL指針,所以將其置空*/
arg[k]=(char *) malloc(sizeof(char));
arg[k]=NULL;
if(is_exe==0 && is_absolute==0) //./可執行文件名 命令的處理
{
if (is_founded(arg[0])==0){/*查找arg[0]中的命令是否存在*/
printf("This command is not founded!\n");
for (i=0;i<=k;i++)
free (arg[i]);
continue;}
}
}
//add_history(input);
/**************************** 執行命令 ******************************/
if ((pid=fork())==0){/*子進程*/
if (is_bg==1)/*若爲後臺命令,等待父進程增加節點*/
while (sig_flag==0)/*若sig_flag==0,等待父進程完成增加節點*/
/*等待父進程SIGUSR1信號,表示節點已加到鏈表中*/
signal(SIGUSR1,setflag);/*收到信號,setflag函數將sig_flag置一
,以跳出循環*/
sig_flag=0;/*置零,爲下一命令作準備*/
if(is_exe==1 || is_absolute==1) //./可執行文件名 以及絕對路徑命令的處理
{
if(execv(arg[0],arg)==-1)
perror("execv failed");
for (i=0;i<=k;i++)
free (arg[i]);
continue;
}
else if(execv(buf,arg)==-1)/*執行命令*/
perror("error");
}
else {/*父進程*/
pid1=pid;/*保存子進程進程號*/
if (is_bg==1) {/*後臺命令*/
add_node(input,pid1);/*增加節點*/
kill(pid,SIGUSR1);/*向子進程發信號,表示節點已加進鏈表*/
pid1=0;/*pid1置零,爲下一命令作準備*/
}
if (is_bg==0)
{
/*前臺命令*/
waitpid(pid,&status,0);
//while(waitpid(pid,&status,WNOHANG)==0)
// {
// signal(SIGINT,dosig);
// }
}
}
if (is_bg==1) sleep(1);/*等待命令(如:ls &)輸出後,再打印Shell提示符*/
for (i=0;i<k;i++)/*釋放空間*/
free (arg[i]);
free(input);
}
return 0;
}
三、友情參考文獻的鏈接
0.secret
Linux Shell腳本面試25個經典問答
https://www.cnblogs.com/gala1021/p/8519456.html
1.shell程序
1)C語言實現一個精簡的shell
https://blog.csdn.net/lishichengyan/article/details/78193131
2)Linux C實現簡單的shell
https://blog.csdn.net/wqx521/article/details/50894661
3)如何實現linux的Shell下按鍵盤的上下方向鍵就可以出現以前的命令?
https://bbs.csdn.net/topics/80237574
http://bbs.chinaunix.net/forum-viewthread-tid-667725.html
4)如何實現按下Tab鍵自動補全匹配的字符串並輸出?
http://bbs.chinaunix.net/thread-656271-1-1.html
5)linux 讀取鍵盤上下左右鍵小程序
https://blog.csdn.net/cean1024/article/details/72903069
6)shell命令解釋器實驗報告
http://www.doc88.com/p-8015468209269.html
7)linux的基本操作(shell 腳本的基礎知識)
https://www.cnblogs.com/zhang-jun-jie/p/9266844.html
2.shell命令
1)Linux Shell常用shell命令
https://www.cnblogs.com/zhangziqiang/p/7478075.html
2)什麼是bash shell的內建(build in)命令
https://blog.csdn.net/trochiluses/article/details/9094517
3)在linux下C語言實現對鍵盤事件的監聽
https://blog.csdn.net/alangdangjia/article/details/27697721
4)Linux下鍵盤鍵值對應input event下的code值表
https://blog.csdn.net/cyf15238622067/article/details/78431709
5)Linux命令自己總結
https://www.cnblogs.com/200911/p/4012161.html
6)真正的Linux命令大全
https://wenku.baidu.com/view/3d63178a112de2bd960590c69ec3d5bbfd0adaed.html
7)在linux中使用getch()函數
https://www.xuebuyuan.com/3221739.html
8)linux環境下,清空history中記錄的歷史命令
https://www.cnblogs.com/chuanzhang053/p/9030834.html
9)linux 清空歷史命令
https://www.cnblogs.com/hellojesson/p/9984518.html
10)Linux下的輸入/輸出重定向
https://www.cnblogs.com/fengkang1008/p/4652265.html
11)Linux中重定向
https://www.cnblogs.com/crazylqy/p/5820957.html
12)Linux的bg和fg命令簡單介紹
https://www.cnblogs.com/oxspirt/p/8583307.html
13)Unix或Linux中&、jobs、fg、bg等命令的使用方法
https://blog.csdn.net/jimmy1357/article/details/46826133
14)linux:幫助命令help、man、info
https://www.cnblogs.com/kumata/p/8993914.html
15)Linux命令類型和執行順序-type命令
https://blog.csdn.net/u010599211/article/details/85335864
16)Linux type命令的用法
https://blog.csdn.net/jiaxinhong/article/details/82826314
17)使用命令把前臺和後臺的進程互相轉換
https://www.cnblogs.com/liu1026/p/8962440.html
18)Linux shell中運行可執行程序後加上&的作用
https://blog.csdn.net/oscarjulia/article/details/70272445
19)GNU Readline 庫及編程簡介
https://www.cnblogs.com/hazir/p/instruction_to_readline.html
20)readline庫的簡單使用
https://blog.csdn.net/xuancbm/article/details/81436681