C语言Shell模拟程序测试求助:模拟终端输入与输出验证问题
问题分析与解决方案
测试代码无法工作的原因
你的test.c存在多个关键问题:
- 进程间通信逻辑错误:调用
system("./shell")后,./shell会进入死循环等待输入,导致测试程序卡在这一步,永远不会执行后面的system("ls -l")。而且system("ls -l")是启动独立进程,和运行中的./shell没有任何交互,根本无法把命令输入到你的shell程序中。 - 重定向的作用范围误解:你在
test.c中用freopen修改的是测试进程自己的stdin/stdout,虽然system("./shell")启动的子进程会继承这些重定向,但如果input.txt为空,./shell会因fgets阻塞等待输入,无法继续执行。 - 断言逻辑完全错误:
ls -l的输出包含文件权限、所有者、大小、修改时间等完整信息,绝非你断言的"shell.c \n shell",直接用strcmp对比必然失败。 - 字符串未正确终止:
fread读取的内容不会自动添加\0终止符,strcmp访问时会出现未定义行为,可能导致程序崩溃或错误判断。
正确的测试方案
方案1:使用管道与shell进程交互(推荐)
通过fork+pipe创建管道,直接控制shell的输入和捕获输出,无需依赖文件:
#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> int main() { int shell_stdin[2], shell_stdout[2]; // 创建输入管道(测试程序写,shell读) pipe(shell_stdin); // 创建输出管道(shell写,测试程序读) pipe(shell_stdout); pid_t pid = fork(); if (pid == 0) { // 子进程:启动shell // 重定向stdin到管道读端 dup2(shell_stdin[0], STDIN_FILENO); // 重定向stdout到管道写端 dup2(shell_stdout[1], STDOUT_FILENO); // 关闭无用的管道端 close(shell_stdin[1]); close(shell_stdout[0]); // 执行shell程序 execl("./shell", "./shell", NULL); perror("exec shell failed"); exit(1); } else { // 父进程:测试逻辑 // 关闭无用的管道端 close(shell_stdin[0]); close(shell_stdout[1]); // 向shell发送命令(注意要加换行,因为shell用fgets读取) const char* cmd = "ls -l\nquit\n"; write(shell_stdin[1], cmd, strlen(cmd)); close(shell_stdin[1]); // 读取shell的输出 char output[4096] = {0}; ssize_t n = read(shell_stdout[0], output, sizeof(output)-1); close(shell_stdout[0]); // 等待shell进程退出 waitpid(pid, NULL, 0); // 验证输出:这里不直接对比完整ls输出(因为时间等会变),而是检查是否包含关键文件名 assert(strstr(output, "shell.c") != NULL); assert(strstr(output, "test.c") != NULL); printf("Test passed!\n"); } return 0; }
方案2:使用文件重定向批量测试
如果需要批量测试多个命令,可以预先准备输入文件和预期输出:
- 创建
test_input.txt,内容为:
ls -l quit
- 编写测试程序:
#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { // 执行shell,重定向输入输出到文件 system("./shell < test_input.txt > test_output.txt"); // 读取输出文件 FILE* fp = fopen("test_output.txt", "r"); assert(fp != NULL); char output[4096] = {0}; fread(output, sizeof(output)-1, 1, fp); fclose(fp); // 验证关键内容 assert(strstr(output, "shell.c") != NULL); assert(strstr(output, "test.c") != NULL); // 清理临时文件 remove("test_output.txt"); printf("Test passed!\n"); return 0; }
额外注意事项
- 测试
ls -l这类输出受环境影响的命令时,不要直接对比完整字符串,而是检查关键标识(如文件名),或者在临时目录中创建固定文件进行测试,避免因文件修改时间、权限等变化导致测试失败。 - 你的
shell.c本身存在缺陷:每次循环都会创建管道和fork,但没有等待子进程退出,会产生僵尸进程;另外read(fildes[0], buf, 1024)可能无法读取ls -l的完整输出(如果输出超过1024字节),需要循环读取。
内容的提问来源于stack exchange,提问作者Dolev Dublon
相关产品推荐
相关产品推荐

