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

父进程调用mini_read时fgets不等待输入,子进程正常的问题求助

问题描述

我有一个用于设置环境变量的函数mini_read,代码如下:

int mini_read(char ** variables){
    if(variables[1] == NULL){
        fprintf(stderr, "error\n");
        return 1;
    }
    char buf[100];
    fgets(buf, 100, stdin);

        //I doubt the bug is below here
    buf[strcspn(buf, "\n")] = 0;
    char ** valores = NULL;
    char * valor = strtok(buf, " \t");
    valores[0] = valor;
    int i = 1;
    // Guardar valores en array
    while(valor != NULL){
        valor = strtok(NULL, " \t");
        valores[i] = valor;
        i++;
    }
    for(int i = 1; ; i++){
        if(valores[i-1] == NULL) break;
        //printf("variables[%d]: %s\n valor[%d]: %c\n", i, variables[i], i, valor[i]);
        if(variables[i] == NULL){
            char*fin = NULL;
            //Si hay más valores que variables, concatenar valores
            for(int j = i-1; valores[j] != NULL; j++){
                printf("%s\n", fin);
                strcat(fin, valores[j]);
            }
            printf("%s\n", fin);
            setenv(variables[i-1], fin, 1);
            break;
        }
        setenv(variables[i], valores[i-1], 1);
    }
    return 0;
}

由于子进程调用该函数无法全局设置环境变量,我希望在父进程中调用它。但测试发现:子进程调用时fgets能正常等待输入,父进程调用时fgets却直接跳过不等待输入。主函数代码如下:

int main(void){
    pid_t pid = fork();

    if(pid == 0){
        //Option 1
        if(strcmp(argv[0], "read") == 0) mini_read(argv);
    }

    else{
        //Option 2
        if(strcmp(argv[0], "read") == 0) mini_read(argv);

        waitpid(pid, &wstatus, 0);      
    }
}

请问该如何解决父进程中fgets不等待输入的问题?


解决方法

1. 核心问题:父子进程的stdin竞争与逻辑错误

fork()会让父子进程共享同一个标准输入文件描述符,你的代码中如果argv[0]为"read",父子进程会同时进入mini_read调用,两者竞争读取stdin,导致父进程的fgets出现异常。正确逻辑是只在父进程中执行mini_read,子进程不处理该逻辑。

修改后的主函数代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char **argv){
    pid_t pid = fork();
    int wstatus;

    if(pid == 0){
        // 子进程执行其他逻辑,不处理read命令
        exit(0);
    }
    else if(pid > 0){
        // 仅父进程处理read命令
        if(strcmp(argv[0], "read") == 0){
            mini_read(argv);
        }
        waitpid(pid, &wstatus, 0);
    }
    else{
        perror("fork failed");
        exit(1);
    }
    return 0;
}

2. 修复mini_read的致命内存错误

原mini_read存在多个内存操作错误,即使解决fgets问题也会崩溃:

  • 空指针valores直接赋值,触发段错误;
  • 空指针fin调用strcat,非法内存访问;
  • strtok循环逻辑错误,导致数组越界。

修复后的mini_read示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int mini_read(char ** variables){
    if(variables[1] == NULL){
        fprintf(stderr, "error: no variable name provided\n");
        return 1;
    }

    char buf[100];
    // 清空stdin残留的输入
    int c;
    while((c = getchar()) != '\n' && c != EOF);
    rewind(stdin);

    if(fgets(buf, sizeof(buf), stdin) == NULL){
        fprintf(stderr, "error reading input\n");
        return 1;
    }

    buf[strcspn(buf, "\n")] = 0;

    // 统计token数量
    int token_count = 0;
    char * temp = strdup(buf);
    if(temp == NULL){
        perror("strdup failed");
        return 1;
    }
    char * token = strtok(temp, " \t");
    while(token != NULL){
        token_count++;
        token = strtok(NULL, " \t");
    }
    free(temp);

    // 分配valores数组内存
    char ** valores = malloc((token_count + 1) * sizeof(char*));
    if(valores == NULL){
        perror("malloc failed");
        return 1;
    }

    // 填充valores数组
    token = strtok(buf, " \t");
    int i = 0;
    while(token != NULL){
        valores[i] = token;
        i++;
        token = strtok(NULL, " \t");
    }
    valores[i] = NULL;

    // 设置环境变量
    for(i = 1; ; i++){
        if(valores[i-1] == NULL) break;

        if(variables[i] == NULL){
            // 拼接剩余参数
            size_t total_len = 0;
            for(int j = i-1; valores[j] != NULL; j++){
                total_len += strlen(valores[j]) + 1;
            }
            char * fin = malloc(total_len);
            if(fin == NULL){
                perror("malloc failed");
                free(valores);
                return 1;
            }
            fin[0] = '\0';
            for(int j = i-1; valores[j] != NULL; j++){
                if(j > i-1) strcat(fin, " ");
                strcat(fin, valores[j]);
            }
            setenv(variables[i-1], fin, 1);
            free(fin);
            break;
        }
        setenv(variables[i], valores[i-1], 1);
    }

    free(valores);
    return 0;
}

3. 额外检查

可以在父进程中检查stdin是否连接到终端,避免非终端输入导致的异常:

if(!isatty(STDIN_FILENO)){
    fprintf(stderr, "error: stdin is not a terminal\n");
    return 1;
}

内容的提问来源于stack exchange,提问作者miguelrr_11

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 23:40:40