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

Ubuntu 20.04自制shell批量执行脚本出现文件不存在报错问题

简易Shell批量执行命令时的文件存在性错误问题分析

问题场景

在Linux Ubuntu 20.04.4系统中,实现了一个简易shell命令解释器(核心代码见下文)。交互式执行以下命令完全正常:

ls > y
cat < y | sort | uniq | wc > y1
cat y1
rm y1
ls | sort | uniq | wc
rm y

但将这些命令保存到t.sh文件后,通过./a.out < t.sh批量执行时,出现错误提示:

5 5 29
5 5 29
cat: y1: No such file or directory
rm: Cannot erase 'y1': no such file or directory
4 4 27
rm: Cannot clear 'y': no such file or directory

实际检查发现cat和rm命令已经正常执行,但仍弹出文件不存在的错误,且相同代码在他人环境中运行正常。

核心实现代码

void runcmd(struct cmd *cmd)
{
  int p[2], r;
  int fd;
  int pid;
  char str[30] ="/bin/";
  FILE * stream;
  struct execcmd *ecmd;
  struct pipecmd *pcmd;
  struct redircmd *rcmd;

  if(cmd == 0)
    _exit(0);

  switch(cmd->type){
  default:
    fprintf(stderr, "unknown runcmd\n");
    _exit(-1);

  case ' ':
    ecmd = (struct execcmd*)cmd;
    if(ecmd->argv[0] == 0){
      _exit(0);
    fprintf(stderr, "exec not implemented\n");
    }
    // Your code here ...
    else if(execvp(ecmd->argv[0],ecmd->argv)==-1){
        ecmd->argv[0] = strcat(str,ecmd->argv[0]);
    
        if(execvp(ecmd->argv[0],ecmd->argv)==-1){
            fprintf(stderr,"file not found\n");
            exit(1);
        }
        
    }
    break;

  case '>':
    rcmd = (struct redircmd*)cmd;
    ecmd = (struct execcmd*)(rcmd->cmd);
    if(0 <(fd = open(rcmd->file,O_CREAT|O_WRONLY,0644))){

        stream = freopen(rcmd->file,"w",stdout);
        runcmd(rcmd->cmd);
        fclose(stream);
        }
    else {
            fprintf(stderr,"file open error\n");
        }
    close(fd);
    stream = NULL;
    break;
  case '<':
    rcmd = (struct redircmd*)cmd;
    // Your code here ...
    if( 0 < (fd = open(rcmd->file,rcmd->flags)))
        stream = freopen(rcmd->file,"r",stdin);
        runcmd(rcmd->cmd);
        fclose(stream);
        close(fd);
    }
    else fprintf(stderr,"file open error\n");
    break;

  case '|':

    pcmd = (struct pipecmd*)cmd;
     // Your code here ...
    struct cmd * lcmd =(pcmd->left);
    struct cmd * rcmd =(pcmd->right);
    int fd[2];

    if(pipe(fd)==-1){
        fprintf(stderr,"pipe error");
        exit(1);
    }
    pid = fork1();

    if(pid == 0){
        dup2(fd[1],1);
        close(fd[0]);
        runcmd(lcmd);
    }

    else{
        dup2(fd[0],0);
        close(fd[1]);
        runcmd(rcmd);
     }
    
    break;
  }
   _exit(0);
}

问题原因分析

1. 重定向处理未恢复父进程标准流

在case '>'和case '<'的重定向逻辑中,调用freopen修改了父进程的stdout/stdin,但执行完命令后没有恢复原始流。批量执行时,父进程的标准输入被重定向到t.sh,当处理cat < y时,父进程的stdin被改成了y文件,后续命令会读取y的内容而非t.sh剩余的命令,导致命令执行顺序混乱,出现文件未创建完成就被访问的情况。

2. 管道处理未等待子进程完成

在case '|'的管道逻辑中,父进程执行右侧命令后直接继续,没有调用waitpid等待子进程结束。批量执行时,父进程会提前触发后续命令(比如cat y1),而此时管道左侧的命令还在创建y1文件,导致文件尚未就绪,触发"文件不存在"的错误。交互式执行时,用户输入间隔给了子进程足够的执行时间,所以不会暴露问题。

3. case '<'存在语法错误

case '<'中的if语句未加花括号,导致runcmd(rcmd->cmd); fclose(stream); close(fd);无论open是否成功都会执行。当open失败时,stream为NULL,调用fclose(stream)会触发未定义行为,进一步加剧错误输出。

4. 文件描述符泄漏

在重定向处理中,open得到的文件描述符fd没有在freopen后及时关闭,子进程会继承这些未关闭的fd,可能导致文件状态同步异常,影响后续命令对文件的判断。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:09:28