實驗一 實現帶參數的簡單shell
1、 實驗名稱,實驗編號和具體名稱
實驗一利用課本第9頁程式1-5的架構,實現帶參數的簡單shell,實現允許輸入命令帶參數的簡單shell。原來的實現是不能夠帶參數的。輸入命令所能帶的參數個數,只受到系統鍵盤輸入緩衝區長度(以及shell輸入緩衝區長度)的限制,該緩衝區的預設長度是4096個位元組。
2、 實驗內容描述
1. 正確理解並使用系統調用fork(),execve()和waitpid(),特別是execve()函數。
fork()函數建立一個新的進程。新進程就是所謂的子進程,它是執行fork()函數的進程(父進程)的“複製”,也就是說,子進程執行的程式與父進程的完全一樣。當fork()函數傳回值為0時表示處於子進程中;而傳回值大於0時表示處於父進程中,此時的傳回值是子進程的進程id。因此,fork()的傳回值可以用來劃分僅僅適合父進程和子進程執行的程式段。fork()函數傳回值為-1時表示出錯。
如果子進程只是運行與父進程完全一樣的程式,那用處是很有限的。要讓子進程運行不同於父進程的程式,就必須調用execve函數,它是所有其他exec函數的基礎。execve函數把調用它的進程的程式,替換成execve函數的參數所指定的程式。運行execve函數成功後,進程將開始運行新的程式,也就是execve函數的參數所指定的程式。
execve函數原型:int execve(const char *path, const char*argv[],const char *envp[]);
其中:
path:要執行的程式路徑名,比如“/bin/ls”,“cd”,“/usr/bin/gcc”等等。
argv:參數表,比如ls命令中可帶的命令列參數-l,-a等。注意,argv的第一個元素必須是要執行的程式(命令)的路徑名。
envp:環境變數表,供要執行的命令使用。實參數用NULL或系統內容變數environ均可。注意,因為environ由系統提供,屬於外部變數,所以說明時必須用“extern”修飾。
例子:
char *argv[] ={“gcc”, “-g”, “-c”, “hello.c”, NULL};
char *argv1[] = {“/bin/ls”,“-l”, “-a”, NULL};
execve(“/usr/bin/gcc”,argv, environ); // 編譯器“hello.c”
execve(“/bin/ls”,argv1, NULL); // 執行命令“ls –l –a”
execve(“/usr/ls”, argv1, NULL); // 出錯,因為目錄/usr/下沒有ls程式。
// 注意,在argv1 的第一個字串“/bin/ls”中,只有ls是有用的。
系統調用waitpid()用於等待子進程結束、擷取子進程的運行狀態,詳細說明在第八章。本實驗僅僅用它使父進程等待子進程結束,因此維持程式1-5的用法即可。
(2)根據簡單shell的輸入,構造execve函數的參數。
根據程式1-5,數組buf儲存使用者的輸入,包括命令和參數。由於shell命令的命令名和各參數之間是用空格分開,因此可以用空格作為分界符。通過一個迴圈可以把buf數組中的命令和各個參數依次分離開來,並賦給數組argv的各元素適當的指標值。argv數組的最後一個指標必須是NULL。接著就可以調用execve(argv[0],argv, environ)來執行使用者輸入的命令。
提示:argv數組中各指標所指向的字串,可以直接利用buf的儲存空間,不需要另外分配記憶體。
3、設計和實現的主要原理、構思、演算法、執行過程或技巧,必要的注釋和說明。
#include "apue.h"
#include <sys/wait.h>
#include <unistd.h>
int main()
{
charbuf[MAXLINE];
char*envp[]={"PATH=/bin",NULL};
char *argv[]={NULL};
charstr[4096][256];
inti,j,k,m,status;
pid_t pid;
char path[256];
printf("%%");
while(fgets(buf,MAXLINE,stdin)!=NULL){
memset(path,0,sizeof(path));//初始化
if(buf[strlen(buf)-1]=='\n')
buf[strlen(buf)-1]=0;
j=i=k=0;
while(buf[j]!='\0'){
if(buf[j]!='') {
for(i=j;buf[i]!=''&&buf[i]!='\0';i++)
str[k][i-j]=buf[i];
str[k][i-j]='\0';
k++;
j=i;
}else {
j++;
}
}
for(i=0;i<k;i++)
argv[i]=str[i];
argv[k]=NULL;
strcat(path,argv[0]);
if((pid=fork())<0){
err_sys("forkerror");
} elseif(pid==0) {
execve(path,argv,envp);
err_ret("couldn'texecute:%s",argv[0]);
exit(127);
}
if((pid=waitpid(pid,&status,0))<0)
err_sys("waitpiderror");
printf("%%");
}
exit(0);
}
3、 實驗結果
實現了常用指令如/bin/ls,/usr/bin/gcc, 輸入錯誤命令能提示出錯並進入下一輪接收命令狀態。可以用Ctrl-C和Ctrl-\結束簡單shell的運行.
來源程式名: prg1.c
可執行程式名:prg1
編譯方法:gcc prg1.c error.c –o prg1
運行樣本:./prg1
/bin/ls -l
如: