为何C代码执行命令时输出为空?如何读取命令全部执行结果?
问题分析与解决
首先你的代码里存在一个明显的拼写错误:
dup2(piepfds[1], STDERR_FILENO);
这里的piepfds是笔误,应该写成pipefds,这个错误会导致stderr无法正确重定向到管道,是需要优先修正的点。
核心问题:execvp失败的错误处理
当你执行不存在的命令时,execvp会调用失败并返回,但你的子进程代码在execvp之后没有任何处理逻辑,直接结束进程,所以没有错误信息输出。而你在shell里直接运行./wow时看到的错误,是shell本身检测到exec调用失败后输出的,并非命令进程自己输出的。
要捕获这种场景的错误,必须在execvp调用后添加错误处理代码——因为execvp只有在失败时才会返回,这段代码只会在命令不存在/无法执行时触发。
修正后的完整代码
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> #include <string.h> int cmd2(char * const *cmd, char * std_out) { char tmp[4096]; int pipefds[2], x; pid_t pid; ssize_t bytes_read; int status; if (pipe(pipefds) == -1){ perror("pipe"); return -1; } if ( (pid = fork()) == -1){ perror("fork"); close(pipefds[0]); close(pipefds[1]); return -1; } if (pid == 0) { // 修正拼写错误,将stderr重定向到管道写端 if (dup2(pipefds[1], STDOUT_FILENO) == -1) { perror("dup2 stdout"); _exit(EXIT_FAILURE); } if (dup2(pipefds[1], STDERR_FILENO) == -1) { perror("dup2 stderr"); _exit(EXIT_FAILURE); } // 关闭管道的所有描述符 close(pipefds[0]); close(pipefds[1]); execvp(cmd[0], cmd); // 走到这里说明execvp失败 perror(cmd[0]); // 将错误信息输出到stderr(已重定向到管道) _exit(EXIT_FAILURE); // 用_exit避免刷新父进程的缓冲区 } else { close(pipefds[1]); // 关闭父进程的管道写端 // 循环读取管道数据,避免一次读不全 bytes_read = 0; while ((x = read(pipefds[0], tmp + bytes_read, sizeof(tmp) - bytes_read - 1)) > 0) { bytes_read += x; } tmp[bytes_read] = '\0'; // 添加字符串结束符 printf("Got %zd bytes\n", bytes_read); if (bytes_read > 0) { printf("Output:\n%s", tmp); } // 等待子进程结束并获取退出状态 waitpid(pid, &status, 0); close(pipefds[0]); // 可以根据status判断子进程是否正常执行 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { printf("Command exited with status %d\n", WEXITSTATUS(status)); } } return 0; }
额外优化点
- 增加
dup2的错误处理,避免重定向失败时的未知行为 - 父进程循环读取管道数据,确保不会遗漏输出内容
- 使用
waitpid替代wait,精准等待目标子进程 - 添加字符串结束符,避免打印垃圾数据
- 处理子进程的退出状态,方便判断命令执行结果
内容的提问来源于stack exchange,提问作者loaded_dypper
相关产品推荐
相关产品推荐

