C语言简易Shell命令管道实现异常:部分多管道命令执行失败
简易Shell管道功能异常问题
我正在用C语言编写类Bash的简易Shell,目前实现命令间的管道功能(即command1 | command2,需要同时运行两个命令,将前一个命令的stdout通过管道连接到后一个命令的stdin)。
目前类似如下简单命令可以正常执行并输出结果:
shell> echo test | cat | cat
该命令能正确打印"test",但更复杂的命令却无法正常运行,例如:
shell> ls -1 / | sort | rev
从管道逻辑而言,该命令和前者并无差异,但前者成功而后者失败。我已经对主进程和子进程进行了全面调试,确认工作和不工作的命令中,进程都以正确的连接方式启动,但仍完全找不到问题原因。
以下是简化后的代码:
// Uncomment to use hardcoded input // #define USE_HARDCODED_INPUT #include <stdlib.h> #include <string.h> #include <stddef.h> // NULL #include <errno.h> // ENOENT #include <stdio.h> // setbuf, printf #include <unistd.h> // exec, fork #include <fcntl.h> // open #include <sys/types.h> // wait #include <sys/wait.h> void set_process_FDs(int input, int output, int error) { if (input) { dup2(input, STDIN_FILENO); close(input); } if (output) { dup2(output, STDOUT_FILENO); close(output); } if (error) { dup2(error, STDERR_FILENO); close(error); } } void child_setup(char **argv, int input, int output, int error) { if (input || output || error) set_process_FDs(input, output, error); execvp(argv[0], argv); perror("exec()"); exit(1); } int launch_process(char **argv, int is_last, int input, int output, int error) { int status; pid_t pid = fork(); switch(pid) { case -1: perror("fork()"); return 0; case 0: child_setup(argv, input, output, error); return 0; default: break; } if (is_last) wait(&status); return 1; } int run_commands(char ***argvv) { int no_commands_ran = 0; int argc; char **argv = argvv[0]; int in_pipe[2]; int out_pipe[2]; for (int i=0; (argv = argvv[i]); ++i) { pipe(out_pipe); if (i == 0) in_pipe[0] = 0; if (!argvv[i+1]) { close(out_pipe[0]); close(out_pipe[1]); out_pipe[1] = 0; } for (argc=0; argv[argc]; ++argc); if (!launch_process(argv, !argvv[i+1], in_pipe[0], out_pipe[1], 0)) break; if (i != 0) { close(in_pipe[0]); close(in_pipe[1]); } in_pipe[0] = out_pipe[0]; in_pipe[1] = out_pipe[1]; no_commands_ran = i + 1; } return no_commands_ran; } extern int obtain_order(); // Obtains an order from stdin int main(void) { char ***argvv = NULL; int argvc; char *filev[3] = {NULL, NULL, NULL}; int bg; int ret; setbuf(stdout, NULL); // Unbuffered setbuf(stdin, NULL); while (1) { #ifndef USE_HARDCODED_INPUT printf("%s", "shell> "); // Prompt ret = obtain_order(&argvv, filev, &bg); if (ret == 0) // EOF { fprintf(stderr, "EOF\n"); break; } if (ret == -1) continue; // Syntax error argvc = ret - 1; // Line if (argvc == 0) continue; // Empty line if (!run_commands(argvv)) continue; // Error executing command #else argvc = 3; char ***argvv1 = calloc(4, sizeof(char*)); argvv1[0] = calloc(3, sizeof(char*)); argvv1[0][0] = strdup("echo"); argvv1[0][1] = strdup("test"); argvv1[1] = calloc(2, sizeof(char*)); argvv1[1][0] = strdup("cat"); argvv1[2] = calloc(2, sizeof(char*)); argvv1[2][0] = strdup("cat"); char ***argvv2 = calloc(4, sizeof(char*)); argvv2[0] = calloc(4, sizeof(char*)); argvv2[0][0] = strdup("ls"); argvv2[0][1] = strdup("-1"); argvv2[0][2] = strdup("/"); argvv2[1] = calloc(4, sizeof(char*)); argvv2[1][0] = strdup("sort"); argvv2[2] = calloc(4, sizeof(char*)); argvv2[2][0] = strdup("rev"); printf("%s", "shell> echo test | cat | cat\n"); if (!run_commands(argvv1)) continue; // Error executing command usleep(500); printf("%s", "shell> ls -1 / | sort | rev\n"); if (!run_commands(argvv2)) continue; // Error executing command printf("%s", "\nNo more hardcoded commands to run\n"); break; #endif } return 0; }
obtain_order()是位于Yacc简易解析器中的函数,用于将Shell输入内容填充到argvv这个argv向量中。如果想要复现问题,只需取消注释代码开头的#define宏定义,即可看到手动输入问题命令时的表现。
内容的提问来源于stack exchange,提问作者Uwirlbaretrsidma.
相关产品推荐
相关产品推荐

