github.com/z0gSh1u/expshellgit
$ some_program "hello, world"
)這裏簡單介紹寫 Shell 時比較關鍵的一些部分,具體請查看源代碼。github
見 show_command_prompt
函數。shell
command_prompt 是在每行最開始顯示的一段與用戶名、路徑等相關的提示信息。ExpShell 顯示的 prompt 形如 [root@localhost tmp]>
。用 > 而非 #、$ 做爲提示符,以區分原生 Shell。緩存
獲取用戶名函數
passwd *pwd = getpwuid(getuid()); string username(pwd->pw_name);
獲取當前目錄ui
getcwd(char_buf, CHAR_BUF_SIZE); string cwd(char_buf);
/
來 split 後取最後一個便可~
,這裏順便把家目錄地址存到全局變量 home_dir
,後續要用到獲取主機名.net
gethostname(char_buf, CHAR_BUF_SIZE); string hostname(char_buf);
localhost.locald.xxx
的形式,也 split 處理一下輸出之便可code
cout << "[" << username << "@" << hostname << " " << cwd << "]> ";
爲存儲解析結果,定義以下四個類:blog
argv[0] argv[1] ...
的普通命令left: cmd* | right: cmd*
cmd_: cmd* > (or <) file
(最基礎的)解析 exec_cmd遞歸
見 parse_exec_cmd
函數。注意這裏使用 string_split_protect
函數來 split 出 argv,這樣能夠保持被引號引發的帶空格的 argument 不被拆分。
解析一條命令
見 parse
函數。採用分治法遞歸地解析命令。
parse
解析右側剩餘,解析結果做爲本層遞歸的右手邊,構建 pipe_cmd解析內建命令
主要支持 cd 、history 和 quit 命令。
exit(0)
便可實現 quitcd ~
cd ~/some_path
的命令,使用 home_dir
替換 ~
chdir
便可主要見 run_cmd
函數。該函數接收一個 cmd*
,遞歸地完成其鏈上全部 cmd 的執行。
對於 exec_cmd
execvp
函數執行命令
execvp
在當前場景最爲合適對於 pipe_cmd
爲 pipe_cmd 的 left 和 right 分別 fork 子進程執行,並使用管道讓這兩個兄弟進程通訊
這張圖很好地說明了父子進程使用管道通訊的方法
依據上圖,不難類比出兄弟進程進行 IPC 的方法以下
對於 redirect_cmd
在一個死循環中讀入當前命令,若是不是 builtin_command,則 fork 子進程進行解析和執行(避免阻塞 ExpShell 自身),執行完成後子進程 exit。
WIFEXITED(status)
爲 0 時表示子進程異常退出,使用 WEXITSTATUS(status)
能夠進一步得到子進程的 exit code