實作一個極簡 Shell,支援提示符顯示、命令讀取、內建命令(exit、cd、ls)解析執行、未知命令提示、循環執行直到 exit。這是理解進程控制、字串解析、系統呼叫的最佳入門專案。
|-> 模擬 $。fgets(buf, SIZE, stdin) 安全讀取整行,避免緩衝區溢位。strncmp 比對前綴,匹配則呼叫對應 handler。while(flag),exit 將旗標歸零結束。#define BUF_SIZE 256
#define CMD_NUM 3
typedef struct {
const char *str;
size_t size;
void (*handler)(char *);
} Command;
void cmd_exit(char *arg) { /* 設定全域 flag=0 */ }
void cmd_cd(char *arg) { /* chdir(arg) 實作 */ }
void cmd_ls(char *arg) { /* system("ls") 或 opendir/readdir 實作 */ }
Command cmd_tbl[CMD_NUM] = {
{"exit", 4, cmd_exit},
{"cd", 2, cmd_cd},
{"ls", 2, cmd_ls}
};
int main() {
char buf[BUF_SIZE];
int flag = 1;
while (flag) {
printf("|->");
if (!fgets(buf, BUF_SIZE, stdin)) break;
// 移除尾部 \n
buf[strcspn(buf, "\n")] = '\0';
int found = 0;
for (int i = 0; i < CMD_NUM; i++) {
if (strncmp(buf, cmd_tbl[i].str, cmd_tbl[i].size) == 0 &&
(buf[cmd_tbl[i].size] == '\0' || buf[cmd_tbl[i].size] == ' ')) {
cmd_tbl[i].handler(buf + cmd_tbl[i].size);
found = 1;
break;
}
}
if (!found) printf("can't find command: %s\n", buf);
}
return 0;
}
strtok 分割 argv)。fork + execvp)。&)。readline 庫)。|->abc can't find command: abc |->cd /tmp cd running |->ls ls running |->exit
Shell 的本質是「讀取-解析-執行」無限循環。從這個微型專案出發,逐步加入進程管理、信號處理、作業控制,即可理解現代 Shell (bash/zsh) 的核心架構。