简易Shell程序问题:执行命令后返回状态始终为1
简易Shell返回状态异常问题排查
我用C语言开发简易Shell时遇到返回状态问题——无论命令执行成功与否,程序始终返回1,预期成功时返回0。以下是代码相关部分及问题分析:
核心代码模块
main函数
负责Shell整体流程:
#include "shell.h" /** * main - entry point * * Return: 0 if succ * */ int main(void) { char *input; char **args; int stat; do { if (isatty(STDIN_FILENO)) write(1, "ous&zoh$ ", 9); /*prompt*/ input = read_line(); if (input == NULL) { exit(EXIT_FAILURE); } args = break_line(input); if (args == NULL) { perror("Error"); free(input); exit(EXIT_FAILURE); } stat = exe_cmd(args); memory(input, args); } while (stat != -1); /*Continue until -1 : run until user type exit*/ return (0); }
read_line函数
读取用户输入:
#include "shell.h" /** * read_line - read input from user * * Return: buffer */ char *read_line() { char *buffer = NULL; size_t len = 0; ssize_t char_read; char_read = getline(&buffer, &len, stdin); if (char_read == -1) { if (isatty(STDIN_FILENO)) perror("error"); if (buffer != NULL) free(buffer); return (NULL); } return (buffer); }
exe_cmd函数
执行命令:
#include "shell.h" /** * exe_cmd - executes cmd * * @args: arg for cmd * * Return: 0 if scc or -1 if error */ int exe_cmd(char **args) { pid_t pid; int stat; int exe; int exit_stat; if (args == NULL || args[0] == NULL) return (0); if (_strcmp(args[0], "exit") == 0) return (-1); pid = fork(); if (pid == 0) { exe = execvp(args[0], args); if (exe == -1) { perror("error"); exit(EXIT_FAILURE); } } else if (pid > 0) { wait(&stat); if (WIFEXITED(stat)) { exit_stat = WEXITSTATUS(stat); return (exit_stat); } else { perror("error"); return (-1); } } else { perror("eroor"); return (-1); } return (0); }
break_line函数
拆分输入为参数数组:
#include "shell.h" /** * break_line - break a line into array * * @line: line that will user give * * Return: args */ char **break_line(char *line) { char **args = NULL; char *token; size_t size = 0; size_t i; if (line == NULL) return (NULL); token = strtok(line, DEL); for (i = 0; token != NULL; i++) { if (i == size) { size = size + 10; args = _realloc(args, 8 * (size + 1), 8); if (args == NULL) return (NULL); } args[i] = token; token = strtok(NULL, DEL); } args[i] = NULL; return (args); }
memory函数
释放内存:
#include "shell.h" /** * memory - to free al memory allocated * @line : line to be free * @args: args to be free */ void memory(char *line, char **args) { if (line != NULL) free(line); if (args != NULL) free(args); }
_realloc函数
内存重分配:
#include <stdlib.h> #include "shell.h" /** * *_realloc - reallocates a memory block using malloc and free * @ptr: pointer to the memory previsouly allocated by malloc * @old_size: size of the allocated memory for ptr * @new_size: new size of the new memory block * Return: pointer to the newly allocated memory block */ void *_realloc(void *ptr, unsigned int old_size, unsigned int new_size) { char *ptr1; char *old_ptr; unsigned int i; if (new_size == old_size) return (ptr); if (new_size == 0 && ptr) { free(ptr); return (NULL); } if (!ptr) return (malloc(new_size)); ptr1 = malloc(new_size); if (!ptr1) return (NULL); old_ptr = ptr; if (new_size < old_size) { for (i = 0; i < new_size; i++) ptr1[i] = old_ptr[i]; } if (new_size > old_size) { for (i = 0; i < old_size; i++) ptr1[i] = old_ptr[i]; } free(ptr); return (ptr1); }
头文件shell.h
#ifndef SHELL_H #define SHELL_H #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <string.h> #define DEL " \t\r\n\a" char *read_line(); char **break_line(char *line); int exe_cmd(char **args); void memory(char *line, char **args); void *_realloc(void *ptr, unsigned int old_size, unsigned int new_size); /*string.h*/ int _strcmp(char *s1, char *s2); #endif
问题根源分析
1. break_line函数的_realloc调用错误
这是导致返回状态异常的核心原因:
args = _realloc(args, 8 * (size + 1), 8);
_realloc的参数顺序是(ptr, old_size, new_size),但此处:
- 错误地将
new_size设为8(仅1个指针的空间),远小于实际需要的size个指针的内存 old_size的计算完全不符合实际内存大小
该错误导致args数组内存分配不足,指针被破坏,最终execvp无法正确执行命令,子进程只能调用exit(EXIT_FAILURE)返回1,父进程拿到的状态码始终为1。
2. 次要问题:硬编码指针大小
代码中用8作为指针大小(64位平台),但32位平台指针大小为4,硬编码会导致跨平台兼容性问题。
解决方案
修复break_line中的_realloc调用
修改扩容逻辑,正确计算内存大小:
if (i == size) { size += 10; // 计算新内存大小:size个char*指针的总字节数 unsigned int new_size = size * sizeof(char*); // 旧内存大小:之前的容量是size-10,对应字节数 unsigned int old_size = (size - 10) * sizeof(char*); args = _realloc(args, old_size, new_size); if (args == NULL) return (NULL); }
- 使用
sizeof(char*)替代硬编码的8,保证跨平台兼容性 - 正确传递
old_size和new_size参数给_realloc
验证exe_cmd逻辑
修复break_line后,execvp能正确接收参数执行命令,子进程的退出状态会如实反映命令执行结果:
- 命令成功时返回0
- 命令失败时返回对应非0状态码
- 仅当
execvp执行失败(如命令不存在)时,子进程返回1
额外改进建议
- 优雅处理EOF:在
main中,当read_line返回NULL时,判断是否为EOF(feof(stdin)),此时应正常退出而非EXIT_FAILURE:
input = read_line(); if (input == NULL) { if (feof(stdin)) exit(EXIT_SUCCESS); else exit(EXIT_FAILURE); }
- 处理信号终止情况:在
exe_cmd中,添加对WIFSIGNALED的判断,处理子进程被信号终止的场景:
wait(&stat); if (WIFEXITED(stat)) { exit_stat = WEXITSTATUS(stat); return (exit_stat); } else if (WIFSIGNALED(stat)) { // 返回信号编号+128,符合Shell惯例 return (128 + WTERMSIG(stat)); } else { perror("error"); return (-1); }
实现内置命令:添加
cd等常用内置命令,提升Shell实用性。内存分配安全检查:在
break_line中,每次_realloc后检查是否分配成功,避免空指针访问。
内容的提问来源于stack exchange,提问作者Oussama El l'amri
相关产品推荐
相关产品推荐

