From 6151327eb4456e82047b93fab2f9defdec0b36ad Mon Sep 17 00:00:00 2001 From: We-unite <3205135446@qq.com> Date: Fri, 7 Jun 2024 15:24:12 +0800 Subject: Add STR type and can "" now. In bash, we can use args with space in it by quote the arg itself, like `grep "test C"` or `cd "dir 1"`. I make it. Add a STR type in flex, and deal with it specially while append it as an argument (malloc a new str space, copy the content of STR into it, then free the STR string, add the new str as an argument). --- .gitignore | 1 + Makefile | 14 +++- README.md | 8 +-- bcsh.c | 209 ++++++------------------------------------------------------ cmd.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd.h | 11 ++-- lex.l | 1 + ohoshell.sh | 2 +- syntax.y | 20 +++--- 9 files changed, 251 insertions(+), 210 deletions(-) create mode 100644 cmd.c diff --git a/.gitignore b/.gitignore index 66c7e33..d26e08e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ * !*.* !*/* +.vscode/* !Makefile *.o *.tab* diff --git a/Makefile b/Makefile index c052fe9..3a3da47 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,11 @@ -bcsh: syntax.tab.o bcsh.c - gcc -g -o bcsh bcsh.c syntax.tab.o -lfl +bcsh: syntax.tab.o bcsh.o cmd.o + gcc -g -o bcsh bcsh.o cmd.o syntax.tab.o -lfl + +bcsh.o: bcsh.c + gcc -g -c bcsh.c -o bcsh.o + +cmd.o: cmd.c + gcc -g -c cmd.c -o cmd.o syntax.tab.o: lex.yy.c syntax.tab.c gcc -g -c syntax.tab.c -o syntax.tab.o @@ -11,4 +17,6 @@ syntax.tab.c: syntax.y bison -o syntax.tab.c -d -v syntax.y -Wcounterexamples clean: - rm -f bcsh lex.yy.c syntax.tab.c syntax.tab.h syntax.output syntax.tab.o + rm -f bcsh + rm -f bcsh.o cmd.o syntax.tab.o + rm -f lex.yy.c syntax.tab.c syntax.tab.h syntax.output diff --git a/README.md b/README.md index d75af6d..e882907 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -[toc] - # 我与 shell -我是个喜欢用 Linux 命令行的人,**喜欢用命令行来完成一切工作,喜欢这种自己独掌系统一切的感觉**,仿佛自己是这台电脑的创世神~~,虽然现实中我只是废柴一个~~。对于每个学习 Linux 的人来说,bash 绝对是一座绕不过去的山,即使已有了 zsh、fish shell 等更加现代化、更加友好的工具,bash 由于其普适性和强大的功能,依然是大多数生产环境的首选。 +我是个喜欢用 Linux 命令行的人,**喜欢用命令行来完成一切工作,喜欢这种自己独掌系统一切的感觉**,仿佛自己是这台电脑的创世神(虽然现实中只是废柴一个)。对于每个学习 Linux 的人来说,bash 绝对是一座绕不过去的山,即使已有了 zsh、fish shell 等更加现代化、更加友好的工具,bash 由于其普适性和强大的功能,依然是大多数生产环境的首选。 但我在学 bash 时遇到了不小的困难,我发现 bash 的语法规定过于奇怪繁杂,完全不像其他编程语言,甚至就连 if 和括号之间都要求必须插入空格,第一次学的时候我在这里犯错,还找了好久。相比之下,我更喜欢 C 语言的语法(也许是我计算机入门学的就是 C 的缘故吧),多简洁明了啊! @@ -10,7 +8,7 @@ 在学习 Linux-0.12 的过程中,我第一次了解到,**原来 shell 和我们自己编写的其他程序一样,是一个独立于操作系统之外的用户态程序**,因而是可以替换的、甚至是可以自己编写的。本来这件事遥遥无期,也许一辈子都不会去做,但我偏偏遇到了网络信息技术大赛,看到了开源鸿蒙 Terminal 应用开发这条赛道。嗯……shell 开发怎么不算 Terminal 应用开发呢?(笑。)于是一个计划生根发芽。 -我的计划是这样的:**我要自己编写一个 shell,这个 shell 要能够完全替代 bash,而且要比 bash 更加简洁易用**。这个 shell 的名字就叫 bcsh,意为“Better C Shell”。我希望通过这个 shell 的开发,能够更加深入地了解 shell 的工作原理,同时也能够提高自己的编程能力。 +我的计划是这样的:**我要自己编写一个 shell,这个 shell 要能够完全替代 bash,而且要比 bash 更加简洁易用**。我希望通过这个 shell 的开发,能够更加深入地了解 shell 的工作原理,同时也能够提高自己的编程能力。 我的设计初衷是,在基本命令与功能上,尽可能地贴近 bash,以免影响我自己的使用习惯;同时,在 case、if、循环等语法上,向 C 语言靠拢,让初学者(主要是我自己)更容易上手。当然,我也会在这个过程中,尽可能地保持代码的简洁性和可读性。因而我为它取了"bcsh"这个名字: @@ -29,7 +27,7 @@ ## TODO list -- Ctrl+C、Ctrl+D、Ctrl+Z 等信号的处理 +- Ctrl+Z 等信号的处理 - 复杂语法的支持:if、case、循环等 - 设置与显示环境变量 - 命令历史记录 diff --git a/bcsh.c b/bcsh.c index 656d608..f1f6b42 100644 --- a/bcsh.c +++ b/bcsh.c @@ -19,199 +19,30 @@ int main(int argc, char **argv) { } void showPrompt() { - printf("\033[01;32m%s" - "\033[35m@%s\033[00m:" - "\033[33m%s\033[00m$ ", - username, hostname, pwd); - fflush(stdout); -} - -Command *newCmd() { - Command *cmd = (Command *)malloc(sizeof(Command)); - cmd->argc = 0; - cmd->args = NULL; - cmd->type = CMD_NORMAL; - cmd->left = NULL; - cmd->right = NULL; - for (int i = 0; i < 3; i++) { - cmd->redirectFile[i] = NULL; - cmd->redirectFD[i] = i; - } - cmd->background = false; - return cmd; -} - -int runCmd(Command *cmd) { - int status; - if (cmd == NULL) { - fprintf(stderr, "cmd is NULL\n"); - return -1; - } - switch (cmd->type) { - case CMD_NORMAL: - status = runNormalCmd(cmd); - break; - case CMD_PIPE: - status = runPipeCmd(cmd); - break; - case CMD_AND: - case CMD_OR: - status = runAndOrCmd(cmd); - break; - } - - freeCmd(cmd); - return status; -} - -int runNormalCmd(Command *cmd) { - pid_t pid; - int status; - if (!strcmp(cmd->args[0], "exit")) { - printf("Good Bye, %s!\n", username); - exit(0); - } else if (!strcmp(cmd->args[0], "cd")) { - if (cmd->argc == 1) { - chdir(getenv("HOME")); - } else { - chdir(cmd->args[1]); - } - printf("\r"); - getcwd(pwd, sizeof(pwd)); + if (getuid() == 0) { + printf("%s@%s:%s# ", username, hostname, pwd); } else { - if ((pid = fork()) == 0) { - // consider the redirection now - // it's just a part of normal command - // but not in other types - redirect(cmd); - exit(execvp(cmd->args[0], cmd->args)); - } else { - if (!cmd->background) { - waitpid(pid, &status, 0); - if (!WIFEXITED(status)) { - // when here, the child process exited abnormally - fprintf(stderr, "Process %d exited with status %d\n", pid, - WEXITSTATUS(status)); - } - } - } - // return the exit status of the child process - return WEXITSTATUS(status); - } -} - -int runPipeCmd(Command *cmd) { - int status; - // create a pipe file and gets two file descriptors - // fd[0] is the read end, while fd[1] the write end - int fd[2]; - pid_t left, right; - pipe(fd); - if ((left = fork()) == 0) { - /* - * close the read end of the pipe, so that the child process - * can only write to it; then redirect the standard output - * to the write end of the pipe, and close the write end - * itself as a file descriptor. - */ - close(fd[0]); - dup2(fd[1], 1); - close(fd[1]); - exit(runCmd(cmd->left)); - } - if ((right = fork()) == 0) { - close(fd[1]); - dup2(fd[0], 0); - close(fd[0]); - exit(runCmd(cmd->right)); - } - // close the pipe, because the parent process doesn't need it - // and wait for the two child processes to exit - close(fd[0]); - close(fd[1]); - waitpid(left, NULL, 0); - waitpid(right, NULL, 0); -} - -int runAndOrCmd(Command *cmd) { - int status; - if (cmd->type == CMD_AND) { - if (fork() == 0) { - exit(runCmd(cmd->left)); - } else { - waitpid(-1, &status, 0); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && fork() == 0) { - status = runCmd(cmd->right); - } - wait(NULL); - } - } else if (cmd->type == CMD_OR) { - if (fork() == 0) { - exit(runCmd(cmd->left)); - } else { - waitpid(-1, &status, 0); - if (WIFEXITED(status) && WEXITSTATUS(status) != 0 && fork() == 0) { - status = runCmd(cmd->right); - } - wait(NULL); - } - } -} - -void redirect(Command *cur) { - int fd; - char *rfile; - for (int i = 0; i < 3; i++) { - fd = cur->redirectFD[i]; - rfile = cur->redirectFile[i]; - if (fd < -1 || fd > 2) { - fprintf(stderr, "file descriptor %d is invalid\n", fd); - exit(1); - } - if (fd == i && rfile == NULL) { - continue; - } - if (fd == -1) { - // append - fd = open(rfile, O_WRONLY | O_CREAT | O_APPEND, 0666); - } else if (rfile != NULL) { - // normal redirection - fd = open(rfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); - } else { - // file descriptor redirection - // when here, fd is surely in 0, 1, 2 - ; - } - - if (fd < 0) { - fprintf(stderr, "open file %s failed\n", rfile); - exit(1); - } - close(i); - dup2(fd, i); - close(fd); + printf("\033[01;32m%s" + "\033[35m@%s\033[00m:" + "\033[33m%s\033[00m$ ", + username, hostname, pwd); } + fflush(stdout); } -void freeCmd(Command *cmd) { - if (cmd == NULL) { - return; - } - if (cmd->left != NULL) { - freeCmd(cmd->left); - } - if (cmd->right != NULL) { - freeCmd(cmd->right); - } - int i; - for (i = 0; i < cmd->argc; i++) { - free(cmd->args[i]); +void argInsert(struct Command *cmd, char *s, bool isStr) { + if (cmd->argc == 0) { + cmd->args = malloc(2 * sizeof(char *)); + } else { + cmd->args = realloc(cmd->args, (cmd->argc + 2) * sizeof(char *)); } - free(cmd->args); - for (i = 0; i < 3; i++) { - if (cmd->redirectFile[i] != NULL) { - free(cmd->redirectFile[i]); - } + if (isStr) { + cmd->args[cmd->argc] = strdup(s + 1); + cmd->args[cmd->argc][strlen(cmd->args[cmd->argc]) - 1] = '\0'; + free(s); + } else { + cmd->args[cmd->argc] = s; } - free(cmd); + cmd->args[cmd->argc + 1] = NULL; + cmd->argc++; } \ No newline at end of file diff --git a/cmd.c b/cmd.c new file mode 100644 index 0000000..e38da41 --- /dev/null +++ b/cmd.c @@ -0,0 +1,195 @@ +#include "cmd.h" + +Command *newCmd() { + Command *cmd = (Command *)malloc(sizeof(Command)); + cmd->argc = 0; + cmd->args = NULL; + cmd->type = CMD_NORMAL; + cmd->left = NULL; + cmd->right = NULL; + for (int i = 0; i < 3; i++) { + cmd->redirectFile[i] = NULL; + cmd->redirectFD[i] = i; + } + cmd->background = false; + return cmd; +} + +void freeCmd(Command *cmd) { + if (cmd == NULL) { + return; + } + if (cmd->left != NULL) { + freeCmd(cmd->left); + } + if (cmd->right != NULL) { + freeCmd(cmd->right); + } + int i; + for (i = 0; i < cmd->argc; i++) { + free(cmd->args[i]); + } + free(cmd->args); + for (i = 0; i < 3; i++) { + if (cmd->redirectFile[i] != NULL) { + free(cmd->redirectFile[i]); + } + } + free(cmd); +} + +int runCmd(Command *cmd) { + int status; + if (cmd == NULL) { + fprintf(stderr, "cmd is NULL\n"); + return -1; + } + switch (cmd->type) { + case CMD_NORMAL: + status = runNormalCmd(cmd); + break; + case CMD_PIPE: + status = runPipeCmd(cmd); + break; + case CMD_AND: + case CMD_OR: + status = runAndOrCmd(cmd); + break; + } + + freeCmd(cmd); + return status; +} + +int runNormalCmd(Command *cmd) { + pid_t pid; + int status; + if (!strcmp(cmd->args[0], "exit")) { + printf("Good Bye, %s!\n", username); + exit(0); + } else if (!strcmp(cmd->args[0], "cd")) { + if (cmd->argc == 1) { + chdir(getenv("HOME")); + } else { + chdir(cmd->args[1]); + } + printf("\r"); + getcwd(pwd, sizeof(pwd)); + } else { + if ((pid = fork()) == 0) { + // consider the redirection now + // it's just a part of normal command + // but not in other commands + redirect(cmd); + exit(execvp(cmd->args[0], cmd->args)); + } else { + if (!cmd->background) { + waitpid(pid, &status, 0); + if (!WIFEXITED(status)) { + if (WIFSIGNALED(status)) { + fprintf(stderr, "Process %d was killed by signal %d\n", + pid, WTERMSIG(status)); + } else { + fprintf(stderr, "Process %d exited with status %d\n", + pid, WEXITSTATUS(status)); + } + } + } + } + // return the exit status of the child process + return WEXITSTATUS(status); + } +} + +int runPipeCmd(Command *cmd) { + int status; + // create a pipe file and gets two file descriptors + // fd[0] is the read end, while fd[1] the write end + int fd[2]; + pid_t left, right; + pipe(fd); + if ((left = fork()) == 0) { + /* + * close the read end of the pipe, so that the child process + * can only write to it; then redirect the standard output + * to the write end of the pipe, and close the write end + * itself as a file descriptor. + */ + close(fd[0]); + dup2(fd[1], 1); + close(fd[1]); + exit(runCmd(cmd->left)); + } + if ((right = fork()) == 0) { + close(fd[1]); + dup2(fd[0], 0); + close(fd[0]); + exit(runCmd(cmd->right)); + } + // close the pipe, because the parent process doesn't need it + // and wait for the two child processes to exit + close(fd[0]); + close(fd[1]); + waitpid(left, NULL, 0); + waitpid(right, NULL, 0); +} + +int runAndOrCmd(Command *cmd) { + int status; + if (cmd->type == CMD_AND) { + if (fork() == 0) { + exit(runCmd(cmd->left)); + } else { + waitpid(-1, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && fork() == 0) { + status = runCmd(cmd->right); + } + wait(NULL); + } + } else if (cmd->type == CMD_OR) { + if (fork() == 0) { + exit(runCmd(cmd->left)); + } else { + waitpid(-1, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) != 0 && fork() == 0) { + status = runCmd(cmd->right); + } + wait(NULL); + } + } +} + +void redirect(Command *cur) { + int fd; + char *rfile; + for (int i = 0; i < 3; i++) { + fd = cur->redirectFD[i]; + rfile = cur->redirectFile[i]; + if (fd < -1 || fd > 2) { + fprintf(stderr, "file descriptor %d is invalid\n", fd); + exit(1); + } + if (fd == i && rfile == NULL) { + continue; + } + if (fd == -1) { + // append + fd = open(rfile, O_WRONLY | O_CREAT | O_APPEND, 0666); + } else if (rfile != NULL) { + // normal redirection + fd = open(rfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + } else { + // file descriptor redirection + // when here, fd is surely in 0, 1, 2 + ; + } + + if (fd < 0) { + fprintf(stderr, "open file %s failed\n", rfile); + exit(1); + } + close(i); + dup2(fd, i); + close(fd); + } +} \ No newline at end of file diff --git a/cmd.h b/cmd.h index ce1d44c..7364936 100644 --- a/cmd.h +++ b/cmd.h @@ -30,16 +30,19 @@ typedef struct Command { bool background; } Command; +extern char hostname[100]; +extern char *username; +extern char pwd[100]; + void showPrompt(); -Command *newCmd(); +void argInsert(struct Command *cmd, char *s, bool isStr); +Command *newCmd(); +void freeCmd(Command *cmd); int runCmd(Command *cmd); int runNormalCmd(Command *cmd); int runPipeCmd(Command *cmd); int runAndOrCmd(Command *cmd); - -void freeCmd(Command *cmd); - void redirect(Command *cur); #endif \ No newline at end of file diff --git a/lex.l b/lex.l index 4f1497a..73883f6 100644 --- a/lex.l +++ b/lex.l @@ -19,6 +19,7 @@ void yyerror(const char *s); ">>" { return APPEND; } "&" { return BACKGROUND; } [0-2]">"&[0-2] { yylval.str = strdup(yytext); return FD_REDIRECT; } +\"[^\"]*\" { yylval.str = strdup(yytext); return STRING; } [a-zA-Z0-9_\-\/\*.]+ { yylval.str = strdup(yytext); return WORD; } "(" { return LPAREN; } ")" { return RPAREN; } diff --git a/ohoshell.sh b/ohoshell.sh index 341b5ad..f45f584 100644 --- a/ohoshell.sh +++ b/ohoshell.sh @@ -36,7 +36,7 @@ esac flex -o lex.yy.c lex.l bison -o syntax.tab.c -d -v syntax.y -Wcounterexamples -$compiler -o bcsh bcsh.c syntax.tab.c -Wall \ +$compiler -o bcsh bcsh.c cmd.c syntax.tab.c -Wall \ --target=$targetPlatform \ --sysroot=$native/sysroot \ -march=$arch -mfloat-abi=softfp \ No newline at end of file diff --git a/syntax.y b/syntax.y index a53537a..7f6745e 100644 --- a/syntax.y +++ b/syntax.y @@ -2,6 +2,7 @@ #include "cmd.h" #include "lex.yy.c" #include + %} %union { @@ -65,16 +66,19 @@ separator: part: part WORD { $$ = $1; - $$->argc++; - $$->args = realloc($$->args, ($$->argc + 1) * sizeof(char *)); - $$->args[$$->argc - 1] = $2; - $$->args[$$->argc] = NULL; + argInsert($$, $2, false); } | WORD { $$ = newCmd(); - $$->args = malloc(2 * sizeof(char *)); - $$->args[$$->argc++] = $1; - $$->args[$$->argc] = NULL; + argInsert($$, $1, false); + } + | part STRING { + $$ = $1; + argInsert($$, $2, true); + } + | STRING { + $$ = newCmd(); + argInsert($$, $1, true); } | part REDIRECT_IN WORD { $$ = $1; @@ -99,4 +103,4 @@ part: } ; -%% +%% \ No newline at end of file -- cgit v1.2.3-70-g09d2