You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

简易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

额外改进建议

  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);
}
  1. 处理信号终止情况:在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);
}
  1. 实现内置命令:添加cd等常用内置命令,提升Shell实用性。

  2. 内存分配安全检查:在break_line中,每次_realloc后检查是否分配成功,避免空指针访问。

内容的提问来源于stack exchange,提问作者Oussama El l'amri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 11:20:55