C程序输出重定向后控制台输出失效问题求助
问题描述
开发一个对比两个命令输出的C程序,使用格式为compare command1 [arguments...] -- command2 [arguments...],需要将两个命令的输出分别重定向到文件A和B,再用diff -u对比。当前代码能正常将输出写入A、B文件,但控制台输出完全失效,添加的调试printf无法在终端显示,推测是标准输出被重定向到文件后未恢复。
用户当前代码:
#include <stdio.h> #include <unistd.h> #include <dirent.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> //Create a process to execute the first command (argv[1]) with its arguments (until you find "--") and redirect it to a file A. Then, from the argument after "--", to the end, execute the second command with its arguments and redirect it to file B. int main(int argc, char* argv[]){ int fd1, fd2; pid_t pid; int i = 1; char* args1[10]; char* args2[10]; while(strcmp(argv[i], "--") != 0){ args1[i-1] = argv[i]; i++; } args1[i-1] = NULL; i++; int j = 0; while(i < argc){ args2[j] = argv[i]; i++; j++; } args2[j] = NULL; fd1 = open("A", O_CREAT | O_WRONLY | O_TRUNC, 0644); fd2 = open("B", O_CREAT | O_WRONLY | O_TRUNC, 0644); pid = fork(); if(pid == 0){ dup2(fd1, 1); close(fd1); close(fd2); execvp(args1[0], args1); } else{ wait(NULL); dup2(fd2, 1); close(fd1); close(fd2); execvp(args2[0], args2); } return 0; }
问题分析
当前代码的核心问题:
- 父进程在等待第一个子进程完成后,直接将自身标准输出重定向到文件B,随后调用
execvp执行第二个命令。这会导致父进程的标准输出被永久修改,所有后续输出(包括调试printf)都会写入文件B,而非控制台。 - 父进程执行
execvp后,进程镜像被完全替换,main函数后续代码(包括return 0)都不会执行,根本没有机会恢复标准输出。 - 缺少对原始标准输出的保存机制,无法在需要时恢复控制台输出。
解决方案
核心思路:
- 保存原始标准输出文件描述符,确保可随时恢复控制台输出。
- 第二个命令也通过子进程执行,避免修改父进程的标准输出。
- 完善系统调用的错误检查,避免静默失败。
- 两个命令执行完成后,父进程自动调用
diff -u输出对比结果。
修改后的代码:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> int main(int argc, char* argv[]){ int fd1, fd2; pid_t pid1, pid2; int i = 1; // 扩大参数数组容量,避免参数过多溢出;也可改用动态分配 char* args1[64]; char* args2[64]; // 保存原始标准输出,用于恢复控制台输出 int original_stdout = dup(1); if (original_stdout == -1) { perror("dup original stdout failed"); exit(EXIT_FAILURE); } // 解析第一个命令及其参数 while(i < argc && strcmp(argv[i], "--") != 0){ args1[i-1] = argv[i]; i++; } // 检查是否存在--分隔符 if (i >= argc || strcmp(argv[i], "--") != 0) { fprintf(stderr, "Error: Missing '--' separator\n"); exit(EXIT_FAILURE); } args1[i-1] = NULL; i++; // 解析第二个命令及其参数 int j = 0; while(i < argc){ args2[j] = argv[i]; i++; j++; } args2[j] = NULL; // 打开输出文件 fd1 = open("A", O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd1 == -1) { perror("open file A failed"); exit(EXIT_FAILURE); } fd2 = open("B", O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd2 == -1) { perror("open file B failed"); close(fd1); exit(EXIT_FAILURE); } // 执行第一个命令(子进程1) pid1 = fork(); if(pid1 == -1){ perror("fork first process failed"); close(fd1); close(fd2); exit(EXIT_FAILURE); } if(pid1 == 0){ dup2(fd1, 1); // 关闭子进程不需要的文件描述符 close(fd1); close(fd2); close(original_stdout); execvp(args1[0], args1); // execvp返回则说明执行失败 perror("execvp first command failed"); exit(EXIT_FAILURE); } // 等待第一个子进程完成 waitpid(pid1, NULL, 0); // 执行第二个命令(子进程2) pid2 = fork(); if(pid2 == -1){ perror("fork second process failed"); close(fd1); close(fd2); exit(EXIT_FAILURE); } if(pid2 == 0){ dup2(fd2, 1); // 关闭子进程不需要的文件描述符 close(fd1); close(fd2); close(original_stdout); execvp(args2[0], args2); perror("execvp second command failed"); exit(EXIT_FAILURE); } // 父进程关闭不需要的文件描述符 close(fd1); close(fd2); // 等待第二个子进程完成 waitpid(pid2, NULL, 0); // 恢复标准输出到控制台(父进程未修改过stdout,此处为冗余保障) dup2(original_stdout, 1); close(original_stdout); // 执行diff命令对比输出文件 printf("=== Comparing output files A and B ===\n"); execlp("diff", "diff", "-u", "A", "B", NULL); // execlp返回则说明执行失败 perror("exec diff failed"); exit(EXIT_FAILURE); return 0; }
关键改动说明
- 保存原始标准输出:用
original_stdout = dup(1)保存控制台输出的文件描述符,确保随时可恢复控制台输出。 - 第二个命令用子进程执行:父进程仅负责创建子进程和等待,自身标准输出始终指向控制台,调试
printf可正常显示。 - 完善错误检查:对
open、fork、execvp等系统调用添加错误处理,便于排查问题。 - 自动执行diff对比:两个命令执行完成后,父进程自动调用
diff -u A B,将对比结果直接输出到控制台,无需手动执行。 - 扩大参数数组容量:将
args1和args2从长度10调整为64,避免参数过多导致数组溢出;也可改用动态分配(如malloc)适配任意数量的参数。
内容的提问来源于stack exchange,提问作者David Monnier
相关产品推荐
相关产品推荐

