日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux C实现简单的shell

發布時間:2023/12/20 linux 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux C实现简单的shell 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Linux C下實現簡單的Shell


宗旨:技術的學習是有限的,分享的精神是無限的。


【需求描述】

用各種C函數實現一個簡單的交互式Shell

1給出提示符,讓用戶輸入一行命令,識別程序名和參數并調用適當的exec函數執行程序,待執行完成后再次給出提示符。

2、識別和處理以下符號:簡單的標準輸入輸出重定向( <>),dup2然后exec管道(|): Shell進程先調用pipe創建一對管道描述符,然后fork出兩個子進程,一個子進程關閉讀端,調用dup2把寫端賦給標準輸出,另一個子進程關閉寫端,調用dup2把讀端賦給標準輸入,兩個子進程分別調用exec執行程序,而Shell進程把管道的兩端都關閉,調用wait待兩個子進程終止。程序應該可以處理以下命令:
○ls
-l-R○>○file1○
○cat○<○file1○|○wc
-c○>○file1○
表示零個或多個空格,表示一個或多個空格

?

//=====================================================================

一、結構定義

?????? 用戶輸入一行命令,將分解后的命令存放在arg中,最多10個。之所以定義輸入、輸出重定向文件名是為了方便使用。還要余外處理重定向和管道問題。必須創建子進程(有多少命令就有多少個子進程),然后調用exec函數族。

typedef struct command {char *arg[SHELL_CMD_MAX_LENGTH]; // 命令行參數,最多10個參數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()函數,重定向處理使用strtok()函數,仔細研究一下他們的區別。

1、管道個數【管道的個數 =(命令個數 1)】

static uint8_t shell_parse_pipe(char *data, cmd_tcmd[]);

2、重定向

static uint8_t shell_parse_cmd_line(char *data,cmd_t *cmd);

填充命令結構體中的各個成員,輸入輸出填充input_file,output_file成員,命令填充arg成員。命令成員的最后一個字符指向NULL。

3、內建命令的單獨處理 --- cd【內建命令都需要一個個處理 --- 如type cd查看命令是內建還是外部shell】

int shell_cd_command(char *command, char *path);

?

/* 只能處理外部命令,內建命令不能處理,內建命令要一個一個單獨實現 type命令可以查看命令是外部命令還是內建命令:type cd // 是shell的內建 */#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]; // 命令行參數,最多10個參數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){/*如果>后面有空格,那么執行完strtok后,空格被替換成'\0',*(p+1)就是'\0',為假,不執行cmd_buf->out=p+1*//*如果>后面沒有空格,那么執行完strtok后,>符號被替換成'\0'了,直接調用strtok函數*/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");/*如果獲取的命令行參數不是>或者<,那么就將它們保存在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){/*以管道符分開的第一個字符串存放在結構數組cmd[0]中,第二個字符串存放在結構數組cmd[1]中,依次遞推*/shell_parse_cmd_line(token, &cmd[count++]);token = strtok_r(NULL, "|", &temp);}return count; }/* cd內部命令 */ 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; // 進程pid/* 輸入輸出重定向文件描述符, 管道個數, 10個管道描述符, 管道分隔的命令個數*/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)); /* 獲取當前工作路徑 */printf("[libang--%s--]$", pathname);fflush(stdout); /* 刷新緩沖區,這連續四行只是為了顯示好看而已,不要也可以 */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內建命令pipe_num = cmd_num - 1;if(pipe_num > SHELL_PIPE_MAX_COUNT){continue;}/* 一個管道有in和out, 創建pipe_num個管道 */for(i = 0; i < pipe_num; ++i){pipe(pipe_fd[i]);}/* 這一輪循環,創建了pipe_num+1個進程,其中一個父進程,pipe_num個子進程 */for(i = 0; i < cmd_num; ++i){if((pid = fork()) < 0){printf("fork fail!\n");exit(1);}if(pid == 0){break;}}/* 有多少個命令就會執行多少個子進程,最終調用exec函數族 */if(pid == 0){/* 上面循環中,子進程break,所以執行下面的語句,此時i就和循環變量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);}/* 管道是進程間通信的一種方式,輸入命令中有管道 */if(pipe_num){/* 第一個子進程,讀入寫出,關閉讀端,把標準輸出重定向到寫端*/if(0 == i){close(pipe_fd[i][0]);dup2(pipe_fd[i][1], STDOUT_FILENO); // 本來執行結果是在標準輸出上close(pipe_fd[i][1]);/* 關閉掉多余的管道 */for(j = 1; j < pipe_num; ++j){close(pipe_fd[j][0]);close(pipe_fd[j][1]);}}/* 最后一個子進程,關閉寫端,把標準輸入重定向到讀端 */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]);/* 關閉掉多余的管道讀寫端 */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個參數是命令,后面的參數是命令選項如:-l */execvp(cmds[i].arg[0], cmds[i].arg);}else // 父進程阻塞{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; }

?

總結

以上是生活随笔為你收集整理的Linux C实现简单的shell的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。