Linux C实现简单的shell
Linux C下實(shí)現(xiàn)簡單的Shell
宗旨:技術(shù)的學(xué)習(xí)是有限的,分享的精神是無限的。
【需求描述】
用各種C函數(shù)實(shí)現(xiàn)一個(gè)簡單的交互式Shell:
1、給出提示符,讓用戶輸入一行命令,識(shí)別程序名和參數(shù)并調(diào)用適當(dāng)?shù)?/span>exec函數(shù)執(zhí)行程序,待執(zhí)行完成后再次給出提示符。
2、識(shí)別和處理以下符號(hào):簡單的標(biāo)準(zhǔn)輸入輸出重定向( <和>),先dup2然后exec。管道(|): Shell進(jìn)程先調(diào)用pipe創(chuàng)建一對(duì)管道描述符,然后fork出兩個(gè)子進(jìn)程,一個(gè)子進(jìn)程關(guān)閉讀端,調(diào)用dup2把寫端賦給標(biāo)準(zhǔn)輸出,另一個(gè)子進(jìn)程關(guān)閉寫端,調(diào)用dup2把讀端賦給標(biāo)準(zhǔn)輸入,兩個(gè)子進(jìn)程分別調(diào)用exec執(zhí)行程序,而Shell進(jìn)程把管道的兩端都關(guān)閉,調(diào)用wait等待兩個(gè)子進(jìn)程終止。程序應(yīng)該可以處理以下命令:
○ls△-l△-R○>○file1○
○cat○<○file1○|○wc△-c○>○file1○
○表示零個(gè)或多個(gè)空格,△表示一個(gè)或多個(gè)空格
?
//=====================================================================
一、結(jié)構(gòu)定義
?????? 用戶輸入一行命令,將分解后的命令存放在arg中,最多10個(gè)。之所以定義輸入、輸出重定向文件名是為了方便使用。還要余外處理重定向和管道問題。必須創(chuàng)建子進(jìn)程(有多少命令就有多少個(gè)子進(jìn)程),然后調(diào)用exec函數(shù)族。
typedef struct command {char *arg[SHELL_CMD_MAX_LENGTH]; // 命令行參數(shù),最多10個(gè)參數(shù)char *input_file; // 存放輸入重定向的文件名char *output_file; // 存放輸出重定向的文件名 } cmd_t;<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">?</span>二、接口定義
管道處理用strtok_r()函數(shù),重定向處理使用strtok()函數(shù),仔細(xì)研究一下他們的區(qū)別。
1、管道個(gè)數(shù)【管道的個(gè)數(shù) =(命令個(gè)數(shù)– 1)】
static uint8_t shell_parse_pipe(char *data, cmd_tcmd[]);
2、重定向
static uint8_t shell_parse_cmd_line(char *data,cmd_t *cmd);
填充命令結(jié)構(gòu)體中的各個(gè)成員,輸入輸出填充input_file,output_file成員,命令填充arg成員。命令成員的最后一個(gè)字符指向NULL。
3、內(nèi)建命令的單獨(dú)處理 --- cd【內(nèi)建命令都需要一個(gè)個(gè)處理 --- 如type cd查看命令是內(nèi)建還是外部shell】
int shell_cd_command(char *command, char *path);
?
/* 只能處理外部命令,內(nèi)建命令不能處理,內(nèi)建命令要一個(gè)一個(gè)單獨(dú)實(shí)現(xiàn) type命令可以查看命令是外部命令還是內(nèi)建命令:type cd // 是shell的內(nèi)建 */#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h>#define SHELL_CMD_MAX_COUNT 10 #define SHELL_CMD_MAX_LENGTH 20 #define SHELL_IN_OUT_FILE_MAX_SIZE 20 #define SHELL_BUFFER 64 #define SHELL_PIPE_MAX_COUNT 10 #define SHELL_PIPE_READ_WRITE 2typedef unsigned int uint32_t; typedef unsigned char uint8_t;typedef struct command {char *arg[SHELL_CMD_MAX_LENGTH]; // 命令行參數(shù),最多10個(gè)參數(shù)char *input_file; // 存放輸入重定向的文件名char *output_file; // 存放輸出重定向的文件名 } cmd_t;/* 以空格符分開命令行字符串 */ static uint8_t shell_parse_cmd_line(char *data, cmd_t *cmd) {uint32_t i = 0;cmd->input_file = NULL;cmd->output_file = NULL;char *token = strtok(data, " ");while(token){/*如果>后面有空格,那么執(zhí)行完strtok后,空格被替換成'\0',*(p+1)就是'\0',為假,不執(zhí)行cmd_buf->out=p+1*//*如果>后面沒有空格,那么執(zhí)行完strtok后,>符號(hào)被替換成'\0'了,直接調(diào)用strtok函數(shù)*/if(*token == '>'){if(*(token + 1)){cmd->output_file = token + 1;}else{cmd->output_file = strtok(NULL, " ");}}else if(*token == '<'){if(*(token + 1)){cmd->input_file = token + 1;}else{cmd->input_file = strtok(NULL, " ");}}else{printf("token....\n");/*如果獲取的命令行參數(shù)不是>或者<,那么就將它們保存在arg中*/cmd->arg[i++] = token;}token = strtok(NULL, " ");}cmd->arg[i] = NULL;return 0; }/* 以管道符分開命令行字符串 */ static uint8_t shell_parse_pipe(char *data, cmd_t cmd[]) {uint8_t count = 0;char *temp;char *token = strtok_r(data, "|", &temp);while(token){/*以管道符分開的第一個(gè)字符串存放在結(jié)構(gòu)數(shù)組cmd[0]中,第二個(gè)字符串存放在結(jié)構(gòu)數(shù)組cmd[1]中,依次遞推*/shell_parse_cmd_line(token, &cmd[count++]);token = strtok_r(NULL, "|", &temp);}return count; }/* cd內(nèi)部命令 */ int shell_cd_command(char *command, char *path) {int return_value = 0;if(strncmp(command, "cd", 2) == 0)if((return_value = chdir(path)) < 0){perror("chdir");}return return_value; }static void shell_process(void) {char cmd_buf[SHELL_BUFFER], pathname[SHELL_BUFFER];cmd_t cmds[SHELL_CMD_MAX_COUNT];pid_t pid; // 進(jìn)程pid/* 輸入輸出重定向文件描述符, 管道個(gè)數(shù), 10個(gè)管道描述符, 管道分隔的命令個(gè)數(shù)*/uint8_t fd_in, fd_out, pipe_num, pipe_fd[SHELL_PIPE_MAX_COUNT][SHELL_PIPE_READ_WRITE], cmd_num = 0;uint8_t i = 0, j = 0;while(1){memset(pathname, 0, sizeof(pathname));getcwd(pathname, sizeof(pathname)); /* 獲取當(dāng)前工作路徑 */printf("[libang--%s--]$", pathname);fflush(stdout); /* 刷新緩沖區(qū),這連續(xù)四行只是為了顯示好看而已,不要也可以 */memset(cmd_buf, 0, sizeof(cmd_buf));fgets(cmd_buf, sizeof(cmd_buf), stdin);cmd_buf[strlen(cmd_buf) - 1] = '\0'; // 獲取輸入的命令到cmd_bufcmd_num = shell_parse_pipe(cmd_buf, cmds);shell_cd_command(cmds[0].arg[0], cmds[0].arg[1]); // 處理cd內(nèi)建命令pipe_num = cmd_num - 1;if(pipe_num > SHELL_PIPE_MAX_COUNT){continue;}/* 一個(gè)管道有in和out, 創(chuàng)建pipe_num個(gè)管道 */for(i = 0; i < pipe_num; ++i){pipe(pipe_fd[i]);}/* 這一輪循環(huán),創(chuàng)建了pipe_num+1個(gè)進(jìn)程,其中一個(gè)父進(jìn)程,pipe_num個(gè)子進(jìn)程 */for(i = 0; i < cmd_num; ++i){if((pid = fork()) < 0){printf("fork fail!\n");exit(1);}if(pid == 0){break;}}/* 有多少個(gè)命令就會(huì)執(zhí)行多少個(gè)子進(jìn)程,最終調(diào)用exec函數(shù)族 */if(pid == 0){/* 上面循環(huán)中,子進(jìn)程break,所以執(zhí)行下面的語句,此時(shí)i就和循環(huán)變量i一樣 */if(cmds[i].input_file){/* 重定向輸入 */if((fd_in = open(cmds[i].input_file, O_RDONLY)) < 0){perror("open fail!\n");}dup2(fd_in, STDIN_FILENO);close(fd_in);}if(cmds[i].output_file){/* 重定向輸出 */if((fd_out = open(cmds[i].output_file, O_RDWR | O_CREAT | O_TRUNC), 0644) < 0){perror("open fail!\n");}dup2(fd_out, STDOUT_FILENO);close(fd_out);}/* 管道是進(jìn)程間通信的一種方式,輸入命令中有管道 */if(pipe_num){/* 第一個(gè)子進(jìn)程,讀入寫出,關(guān)閉讀端,把標(biāo)準(zhǔn)輸出重定向到寫端*/if(0 == i){close(pipe_fd[i][0]);dup2(pipe_fd[i][1], STDOUT_FILENO); // 本來執(zhí)行結(jié)果是在標(biāo)準(zhǔn)輸出上close(pipe_fd[i][1]);/* 關(guān)閉掉多余的管道 */for(j = 1; j < pipe_num; ++j){close(pipe_fd[j][0]);close(pipe_fd[j][1]);}}/* 最后一個(gè)子進(jìn)程,關(guān)閉寫端,把標(biāo)準(zhǔn)輸入重定向到讀端 */else if(pipe_num == i){close(pipe_fd[i - 1][0]);dup2(pipe_fd[i - 1][0], STDIN_FILENO);close(pipe_fd[i - 1][0]);/* 關(guān)閉掉多余的管道讀寫端 */for(j = 0; j < pipe_num - 1; ++j){close(pipe_fd[j][0]);close(pipe_fd[j][1]);}}/* 1~pipe_num-1, */else{dup2(pipe_fd[i - 1][0], STDIN_FILENO);close(pipe_fd[i - 1][0]);dup2(pipe_fd[i][1], STDOUT_FILENO);close(pipe_fd[i][1]);for(j = 0; j < pipe_num; ++j){if((j != i - 1) || j != i){close(pipe_fd[j][0]);close(pipe_fd[j][1]);}}}}/* arg第1個(gè)參數(shù)是命令,后面的參數(shù)是命令選項(xiàng)如:-l */execvp(cmds[i].arg[0], cmds[i].arg);}else // 父進(jìn)程阻塞{for(i = 0; i < pipe_num; ++i){close(pipe_fd[i][0]);close(pipe_fd[i][1]);}for(i = 0; i < cmd_num; ++i){wait(NULL);}}} }int main(int argc, char **argv) {shell_process();return 0; }?
總結(jié)
以上是生活随笔為你收集整理的Linux C实现简单的shell的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab 离散系统稳定性,基于LMI
- 下一篇: matlab|已知多点坐标,求两两之间的