自制Shell中ls/pwd命令异常问题排查求助
问题分析与修复方案
你的自制Shell核心问题出在命令解析逻辑完全没被调用,再加上几个小bug共同导致了ls和pwd的异常。咱们一步步拆解解决:
核心问题:用户输入未被解析到参数数组
你写了parsowanie函数来拆分命令行参数,但在main里读完用户输入的co_chcesz后,完全没调用这个函数!这导致argv数组一直是初始化的全0值,execvp拿到的是空参数,自然触发各种错误:
- 对于
ls:空参数让它误以为你要访问空路径,所以报cannot access '' - 对于
pwd:错误的参数格式让它认为你传了无效的非选项参数,报ignoring non-option arguments
其他需要修复的小问题
未初始化的循环变量
初始while(strcmp(co_chcesz,"exit\n"))时,co_chcesz是栈上的垃圾值,会导致不可预测的循环行为,需要先初始化它。waitpid的位置错误
你把waitpid放在了子进程的分支里,但execvp成功后会直接替换子进程的地址空间,这段代码根本不会执行。而且子进程不该等待自己,waitpid必须放在父进程中。fgets缓冲区大小不匹配co_chcesz的大小是500,但你调用fgets(co_chcesz,1000,stdin),存在缓冲区溢出风险,应该用sizeof(co_chcesz)作为输入长度限制。参数列表终止符不规范
parsowanie函数最后用*argv = '\0';标记参数结束,虽然在C里'\0'等价于NULL指针,但直接用NULL更清晰规范。
修复后的完整代码
#include <sys/types.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> void parsowanie(char *line, char **argv) { while (*line != '\0') { // 跳过空白字符,用'\0'分隔参数 while (*line == ' ' || *line == '\n'|| *line == '\t' ) *line++ = '\0'; // 记录当前参数的起始地址 *argv++ = line; // 移动到下一个空白字符 while (*line != '\0' && *line!= ' ' && *line != '\t' && *line != '\n') line++; } // 用NULL标记参数列表结束(规范写法) *argv = NULL; } int main() { printf("MicroShell\nAutor:Amadeusz Lewandowski\n"); char co_chcesz[500], cwd[500], login[50]; char *argv[1000]; pid_t pid; int status; // 初始化输入缓冲区,避免第一次strcmp读取垃圾值 co_chcesz[0] = '\0'; while(strcmp(co_chcesz,"exit\n") != 0) { sleep(1); getcwd(cwd, sizeof(cwd)); getlogin_r(login, sizeof(login)); printf("[%s:%s]\n$ ", login, cwd); // 读取用户输入,使用正确的缓冲区大小 if (fgets(co_chcesz, sizeof(co_chcesz), stdin) == NULL) { // 处理EOF情况(比如Ctrl+D) break; } // 关键:调用解析函数,把输入拆分成argv参数数组 parsowanie(co_chcesz, argv); // 如果用户只输入了空白,直接跳过循环 if (argv[0] == NULL) { continue; } // 内置命令help单独处理,无需fork子进程 if (strcmp(argv[0], "help") == 0) { printf("autor to miranda orange\n"); continue; } pid = fork(); if (pid == 0) { // 子进程:执行外部命令 if (execvp(argv[0], argv) < 0) { perror("The following error occurred"); printf("Value of errno:%d\n", errno); // 子进程执行失败必须退出,避免继续运行shell逻辑 exit(EXIT_FAILURE); } } else if (pid > 0) { // 父进程:等待子进程执行完成 waitpid(pid, &status, 0); } else { // 处理fork失败的情况 perror("fork failed"); } } return 0; }
修复后验证
- 输入
ls:会正确解析参数,execvp调用正常的ls命令,列出当前目录内容 - 输入
pwd:正确调用pwd,不再报非选项参数错误 help内置命令和exit退出逻辑都能正常工作
内容的提问来源于stack exchange,提问作者Amadeusz Lewandowski
相关产品推荐
相关产品推荐

