管道/ dup2()无法正常工作(在C中实现Unix Shell)

时间:2018-04-20 20:07:05

标签: c unix fork piping dup2

我先发布我的代码,然后解释我遇到的问题:

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

#define MAX_ARGS 20
#define BUFSIZE 1024

int get_args(char* cmdline, char* args[])
{
  int i = 0;

  /* if no args */
  if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
    return 0;

  while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
    if(i >= MAX_ARGS) {
      printf("Too many arguments!\n");
      exit(1);
    }
  }
  /* the last one is always NULL */
  return i;
}

void execute(char* cmdline)
{
  int pid, async, oneapp;
  char* args[MAX_ARGS];
  char* args2[] = {"-l", NULL};
  int nargs = get_args(cmdline, args);
  if(nargs <= 0) return;

  if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
    exit(0);
  }

  printf("before the if\n");
  printf("%s\n",args[nargs - 2]);
  int i = 0;

// EDIT: THIS IS WHAT WAS SUPPOSED TO BE COMMENTED OUT
/*
  while (args[i] != ">" && i < nargs - 1) {
      printf("%s\n",args[i]);
      i++;
  }
*/
  // Presence of ">" token in args
  // causes errors in execvp() because ">" is not
  // a built-in Unix command, so remove it from args
  args[i - 1] = NULL;

  printf("Escaped the while\n");

// File descriptor array for the pipe
int fd[2];

// PID for the forked process
pid_t fpid1;

// Open the pipe
pipe(fd);

// Here we fork
fpid1 = fork();

if (fpid1 < 0)
{
    // The case where the fork fails
   perror("Fork failed!\n");
   exit(-1);
}
else if (fpid1 == 0)
{
       //dup2(fd[1], STDOUT_FILENO);
       close(fd[1]);
       //close(fd[0]);

       // File pointer for the file that'll be written to
       FILE * file;

       // freopen() redirects stdin to args[nargs - 1],
       // which contains the name of the file we're writing to
       file = freopen(args[nargs - 1], "w+", stdin);

       // If we include this line, the functionality works
       //execvp(args[0],args);

       // We're done writing to the file, so close it
       fclose(file);

       // We're done using the pipe, so close it (unnecessary?)
       //close(fd[1]);
}
else
{
   // Wait for the child process to terminate
   wait(0);
   printf("This is the parent\n");

   // Connect write end of pipe (fd[1]) to standard output
   dup2(fd[1], STDOUT_FILENO);

   // We don't need the read end, so close it
   close(fd[0]);

   // args[0] contains the command "ls", which is
   // what we want to execute
   execvp(args[0], args);

   // This is just a test line I was using before to check
   // whether anything was being written to stdout at all
   printf("Exec was here\n");
}

// This is here to make sure program execution
// doesn't continue into the original code, which
// currently causes errors due to incomplete functionality
exit(0);

  /* check if async call */
  printf("Async call part\n");
  if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; }
  else async = 0;

  pid = fork();
  if(pid == 0) { /* child process */
    execvp(args[0], args);
    /* return only when exec fails */
    perror("exec failed");
    exit(-1);
  } else if(pid > 0) { /* parent process */
    if(!async) waitpid(pid, NULL, 0);
    else printf("this is an async call\n");
  } else { /* error occurred */
    perror("fork failed");
    exit(1);
  }
}

int main (int argc, char* argv [])
{
  char cmdline[BUFSIZE];

  for(;;) {
    printf("COP4338$ ");
    if(fgets(cmdline, BUFSIZE, stdin) == NULL) {
      perror("fgets failed");
      exit(1);
    }
    execute(cmdline) ;
  }
  return 0;
}

那么,问题是什么?简单:上面的代码创建了一个具有预期名称的文件,即命令行中提供的名称,该名称放在args [nargs - 1]中。例如,运行程序然后键入

  

ls&gt;的test.txt

创建一个名为test.txt的文件...但它实际上并没有向它写任何东西。我确实设法让程序将垃圾字符打印到文件中多次,但这只发生在绝望的冰雹玛丽编码中,我基本上只是试图让程序将SOMETHING写入文件。

我确实认为我已经设法将问题的原因缩小到代码的这个区域:

else if (fpid1 == 0)
{
       printf("This is the child.\n");

       //dup2(fd[1], STDOUT_FILENO);
       close(fd[1]);
       //close(fd[0]);

       // File pointer for the file that'll be written to
       FILE * file;

       // freopen() redirects stdin to args[nargs - 1],
       // which contains the name of the file we're writing to
       file = freopen(args[nargs - 1], "w+", stdout);

       // If we include this line, the functionality works
       //execvp(args[0],args);

       // We're done writing to the file, so close it
       fclose(file);

       // We're done using the pipe, so close it (unnecessary?)
       //close(fd[1]);
}
else
{
   // Wait for the child process to terminate
   wait(0);
   printf("This is the parent\n");

   // Connect write end of pipe (fd[1]) to standard output
   dup2(fd[1], STDOUT_FILENO);

   // We don't need the read end, so close it
   close(fd[0]);

   // args[0] contains the command "ls", which is
   // what we want to execute
   execvp(args[0], args);

   // This is just a test line I was using before to check
   // whether anything was being written to stdout at all
   printf("Exec was here\n");
}

更具体地说,我认为问题在于我使用(或尝试使用)dup2()和管道功能的方式。我基本上是通过消除过程找到的。我花了几个小时评论事情,移动代码,添加和删除测试代码,我发现了以下内容:

1。)删除对dup2()的调用并使用execvp(args [0],args)将ls命令的结果输出到控制台。父进程和子进程正确地开始和结束。因此,对execvp()的调用正常工作。

2。)行

file = freopen(args[nargs - 1], "w+", stdout)

成功创建一个具有正确名称的文件,因此对freopen()的调用不会失败。虽然现在没有立即证明此函数正常工作,但请考虑事实#3:

3.)在子进程块中,如果我们将freopen从stdin(而不是stdout)重定向到输出文件并取消对execvp(args [0],args)的调用,如下所示:

   // freopen() redirects stdin to args[nargs - 1],
   // which contains the name of the file we're writing to
   file = freopen(args[nargs - 1], "w+", stdin);

   // If we include this line, the functionality works
   execvp(args[0],args);

并运行程序,然后它工作,并且ls命令的结果成功写入输出文件。知道了这一点,似乎可以说freopen()也不是问题。

换句话说,我唯一能够成功做的就是将父进程中执行的execvp()调用的输出传递给stdout,然后从stdout传递到文件使用freopen()。

感谢任何帮助。我昨天上午10点以来一直在这,我完全没有想法。我只是不知道自己做错了什么。为什么这不起作用?

0 个答案:

没有答案