1.我們讓子進程執行cd ..命令的時候,為什么我們執行pwd命令的時候,還是和之前一樣,路徑沒有變化?
本質就是,我們更改的是子進程的環境變量pwd,沒有改變父進程的。當執行pwd時,這個進程的環境變量還是由父進程來的。而父進程的環境變量沒有改變,所以pwd出來的結果也沒有改變。
2.環境變量是由shell自己維護的。
3.在這個簡易版shell中,我們沒有維護這個環境變量,還是依靠系統的環境變量。當環境沒有修改,用的還是系統的環境變量,更改時才寫時拷貝。
4.echo命令也是內鍵命令,因為子進程不會繼承父進程的本地環境變量。所以要打印本地變量,只有由shell來做。
打印命令行提示符(PrintCommandLine):
啟動shell程序,就是創建-bash進程,本質就是一個進程。
首先看到命令行的提示符是這樣的:
包括: 1.用戶名。2.主機名。3.當前的路徑。4.命令行提示符。($)

1.USER和LOGNAME(用戶名):2.HOSTNAME和PWD:
通過環境變量獲取這些信息。
三個函數獲得用戶信息(GetLOGNAME),主機信息(GetHOSTNAME),當前路徑信息(GetPWD)。
通過String進行傳遞,如果要誰用c語言的字符串,就通過c_str()進行獲得底層char。
代碼語言:JavaScript代碼運行次數:0運行復制
const size_t basesize=1024; //獲取用戶名信息 string GetLOGNAME() { string log_name=getenv("LOGNAME"); return log_name.empty()?"None":log_name; } //獲取當前主機信息 string GetHSOTNAME() { string host_name=getenv("HOSTNAME"); return host_name.empty()?"None":host_name; } //獲取當前路徑信息 string GetPWD() { string pwd=getenv("PWD"); if(pwd.empty()) return "None"; string separator="/"; string sub_str=pwd.substr(pwd.find_last_of(separator)+1); return sub_str; } //生成command_line字符串 string MakeCommandLine() { //[kym@hcss-ecs-32c9 2024-11-26]$ char command_line[basesize]; snprintf(command_line,basesize,"[%s@%s %s]@", GetLOGNAME().c_str(), GetHSOTNAME().c_str(), GetPWD().c_str()); return command_line; } // //1.打印命令行提示符 void PrintCommandLine() { printf("%s",MakeCommandLine().c_str()); fflush(stdout); }
獲取命令行信息(GetCommadLine)代碼語言:javascript代碼運行次數:0運行復制
bool GetCommandLine(char command_buff[],size_t size){ char* result=fgets(command_buff,size,stdin); if(result==NULL) return false; result[strlen(result)-1]=0; return true;}
讀取失敗返回false,讀取成功把換行符覆蓋。
讀取時,處理換行符:
如果我們以一行來讀取字符串,那么最會就有換行符,換行符也會讀取進來。所以打印的時候,我們沒有加換行,也是來到了新的一行。
解決辦法:
在返回前,把n置為0。
代碼語言:javascript代碼運行次數:0運行復制
result[strlen(result)-1]=0;
解析命令行(ParseCommandLine)
通過strtok函數進行分解字符串。

代碼語言:javascript代碼運行次數:0運行復制
bool ParseCommandLine(char command_buff[],int len) { (void)len; argc=0; //初始化 memset(argv,0,sizeof(argv)); const char* sep=" "; argv[argc++]=strtok(command_buff,sep); while((bool)(argv[argc++]=strtok(nullptr,sep))); argc--; return false; }
sep表示分隔符,用來把字符串進行分解。但是在分解之前,我們需要把argc和argv進行初始化。每次執行新的命令。
strtok函數第一次調用的時候,str指向要分割的字符串。在后續的調用中傳NULL。因為strtok函數內部有靜態變量維護當前字符串的位置。
執行命令(ExecuteCommand)
通過子進程發生程序替換。當id==0,表示子進程。如果發生了程序替換,還執行了原來的exit,就表示發生錯誤。然后就是父進程子進程。
代碼語言:javascript代碼運行次數:0運行復制
bool ExecuteCommand() { pid_t id=fork(); if(id<0) return false; if(id==0) { execvp(argv[0],argv); exit(1); } int status=0; pid_t rid=waitpid(id,&status,0); if(rid<0) { //等待失敗 } else { return true; } return false; }
哪些命令可以讓子進程執行,哪些命令不能讓子進程執行?為什么?
當執行cd命令時,為什么pwd沒有改變?
我們寫的超簡易版shell中,讓子進程去執行cd .. 。但是其他的進程不是由子進程產生的,不會繼承子進程的環境變量,還是去繼承父進程的環境變量。雖然在子進程中改了環境變量,但是沒有得到進程,所以不起作用。在真正的shell中,就是要去改變shell的環境變量。因為其他的進程都是bash的子進程。
通過getcwd獲取實時的cwd,通過putenv導入環境變量:代碼語言:javascript代碼運行次數:0運行復制
//獲取當前路徑信息string GetPWD(){ if(nullptr==getcwd(cwd,sizeof(cwd))) return "None"; snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",cwd); //更新env putenv(pwdenv); return cwd;}

myshell維護自己的環境變量:
環境變量和本地變量是存在于shell中的一個表。這兩張表就是全局的char*數組。指向很多的字符串。后序我們碰見要導入環境變量的命令,我們直接在shell中的genv數組指向一個堆空間,然后把第一個為空的數組指向這個字符串。
為什么要新申請空間,然后拷貝,再指向?
因為我們每執行一個命令,argv都是會變化的。如果我們直接讓genv的元素指向argv的元素,以后會發生變化。這樣環境變量就找不到了。