为什么这个程序输出的行重复?

时间:2014-05-13 03:22:55

标签: c file-io

好的,我正在努力实现一个简单的历史记录。部分要求是将所有命令输出到每行编号的文件中。

我目前还有其他一切正常运行,但是文件的输出给了我一个非常具体的问题。它正在保存命令并将它们放在编号的文件中,但它为每个命令提供了大约3个重复的副本。例如:

1 hello
1 hello
1 hello
2 there1 hello
2 there3 how1 hello
2 there3 how4 are1 hello
2 there3 how4 are5 you

我搜索过本网站上的上述主题,并阅读文件i / o。据我所知,我正确地做到了:

#include "parser.h"
#include "shell.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct node {
   char info[MAXINPUTLINE]; //Struct setup for linked list.
   struct node *link;
} *start;

void create(char[]);     //Function prototypes.
void insert_end(char[]);
void last_cmd(char[]);    

int main(void) {
   char input[MAXINPUTLINE];
   int count = 2;
   start=NULL;
   char data[MAXINPUTLINE];
   FILE *file;
   file = fopen(".simpleshell_history", "w");
   char *history="history";
   char *last="!!";
   signal_c_init();

   printf("Welcome to the sample shell!  You may enter commands here, one\n");
   printf("per line.  When you're finished, press Ctrl+D on a line by\n");
   printf("itself.  I understand basic commands and arguments separated by\n");
   printf("spaces, redirection with < and >, up to two commands joined\n");
   printf("by a pipe, tilde expansion, and background commands with &.\n\n");

   printf("\nclsh$ ");

   fgets(data, sizeof(data), stdin);
   create(data);
   fprintf(file, "%d ", 1);
   fprintf(file, "%s", data);
   parse(data);

   printf("\nclsh$ ");

   while (fgets(input, sizeof(input), stdin)) {
      stripcrlf(input);
      parse(input);

      insert_end(input);
      fprintf(file, "%d ", count);
      fprintf(file, "%s", input);

      count++;
      printf("\nclsh$ ");
   }
   fclose(file);
   return 0;
}

//Functions below.

//Creation Function.
void create(char data[])
{
   struct node *temp;
   temp = (struct node *)malloc(sizeof(struct node));

   if (start == NULL)
   {
      strcpy(temp->info, data);
      temp->link=NULL;
      start=temp;
   }
}

//Insertion function
void insert_end(char data[])
{
   struct node *ptr, *tempnode;
   ptr = start;

   while(1)
   {
      if(ptr->link != NULL)
      {
         ptr=ptr->link;
      }
      else
         break;
   }
   tempnode=(struct node *)malloc(sizeof(struct node));
   strcpy(tempnode->info, data);
   tempnode->link=NULL;
   ptr->link=tempnode;
}

//Last command repeat function
void last_cmd(char input[])
{
   struct node *ptr;
   ptr = start;
   int i;
   int length=0;
   char temp[MAXINPUTLINE];

   while (ptr!=NULL)
   {
      ptr=ptr->link;
      length++;
   }

   ptr=start;

   for (i=0; i<length-1; i++)
   {
      ptr=ptr->link;
      strcpy(temp, ptr->info);
   }
   printf("%s", temp);
   parse(temp);
}

据我所知,我之前的一些问题并没有得到很好的解决,但我在这方面做了最大努力,我只是把这个问题作为最后的手段。我希望我以一种不烦人或违反任何规则的方式做到这一点,并且可以努力提高我在这里的声誉。

谢谢。

编辑:好的,所以我已将问题隔离到解析输入的文件中。它导致了重复的行,但我无法弄清楚这一点是怎么回事。任何帮助都表示赞赏,但不管怎样,我都会继续这样做,再次感谢所有提示和建议。

分析器:

#include "parser.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>

/* Static Variables Referenced only in this file */
static int pipefd[2];
static int background;
void parse(char *cmdline)
{
  char *cmdpart[2];

  pipefd[0] = PARSE_NOPIPE;    /* Init: default is no pipe */

  background = checkbackground(cmdline);

  /* Separate into individual commands if there is a pipe symbol. */

  if (strstr(cmdline, "|"))
   pipefd[0] = PARSE_USEPIPE;

  /* Must do the strtok() stuff before calling parse_cmd because
     strtok is used in parse_cmd or the functions parse_cmd calls. */

  cmdpart[0] = strtok(cmdline, "|");
  cmdpart[1] = strtok((char *)NULL, "|");
  parse_cmd(cmdpart[0]);
  if (cmdpart[1]) parse_cmd(cmdpart[1]);
}

/* parse_cmd will do what is necessary to separate out cmdpart and run
   the specified command. */

void parse_cmd(char *cmdpart)
{
  int setoutpipe = 0;        /* TRUE if need to set up output pipe
                   after forking */
  int pid;            /* Set to pid of child process */
  int fd;            /* fd to use for input redirection */

  char *args[MAXARGS + 5];
  char *filename;            /* Filename to use for I/O redirection */

  splitcmd(cmdpart, args);

  if (pipefd[0] == PARSE_USEPIPE) {
    pipe(pipefd);
    setoutpipe = 1;
  }

  pid = fork();
  if (!pid) {            /* child */
    if (setoutpipe) {
      dup2(pipefd[1], 1);    /* connect stdout to pipe if necessary */
    }
    if (!setoutpipe && (pipefd[0] > -1)) {
      /* Need to set up an input pipe. */
      dup2(pipefd[0], 0);
    }

    filename = parseredir('<', args);

    if (filename) {    /* Input redirection */
      fd = open(filename, O_RDONLY);
      if (!fd) {
       fprintf(stderr, "Couldn't redirect from %s", filename);
       exit(255);
      }
      dup2(fd, 0);
    }

    if ((filename = parseredir('>', args))) { /* Output redirection */
      fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
      if (!fd) {
       fprintf(stderr, "Couldn't redirect to %s\n", filename);
       exit(255);
      }
      dup2(fd, 1);
    }

    if (!args[0]) {
      fprintf(stderr, "No program name specified.\n");      
      exit(255);
    }

    execvp(args[0], args);
    /* If failed, die. */
    exit(255);
  } else {            /* parent */
    if ((!background) &&
       (!setoutpipe))
      waitpid(pid, (int *)NULL, 0);
    else
      if (background)
       fprintf(stderr, "BG process started: %d\n", (int) pid);
    if (pipefd[0] > -1) {    /* Close the pipe if necessary. */
      if (setoutpipe)
        close(pipefd[1]);
      else                     
        close(pipefd[0]);
    }                           
  } /* if (!pid) */
  freeargs(args);
} /* parse_cmd()  */

/* splitcmd() will split a string into its component parts.

   Since splitcmd() uses strdup, freeargs() should be called on the
   args array after it is not used anymore. */

void splitcmd(char *cmdpart, char *args[])
{
  int counter = 0;
  char *tempstr;

  tempstr = strtok(cmdpart, " ");
  args[0] = (char *)NULL;
  while (tempstr && (counter < MAXARGS - 1)) {
    args[counter] = strdup(expandtilde(tempstr));
    args[counter + 1] = (char *)NULL;
    counter++;
    tempstr = strtok(NULL, " ");
  }
  if (tempstr) {         /* Broke out of loop because of num of args */
    fprintf(stderr, "WARNING: argument limit reached, command may be truncated.\n");
  }
}

/* expandtilde() will perform tilde expansion on str if necessary. */

char *expandtilde(char *str)
{
  static char retval[MAXINPUTLINE];
  char tempstr[MAXINPUTLINE];
  char *homedir;
  char *tempptr;
  int counter;


  if (str[0] != '~') return str;      /* No tilde -- no expansion. */
  strcpy(tempstr, (str + 1));          /* Make a temporary copy of the string */
  if ((tempstr[0] == '/') || (tempstr[0] == 0))
    tempptr = (char *)NULL;
  else {                  /* Only parse up to a slash */
    /* strtok() cannot be used here because it is being used in the function
       that calls expandtilde().  Therefore, use a simple substitute. */
    if (strstr(tempstr, "/"))
      *(strstr(tempstr, "/")) = 0;
    tempptr = tempstr;
  }

  if ((!tempptr) || !tempptr[0]) {    /* Get user's own homedir */
    homedir = gethomedir();
  } else {                  /* Get specified user's homedir */
    homedir = getuserhomedir(tempptr);
  }

  /* Now generate the output string in retval. */

  strcpy(retval, homedir);          /* Put the homedir in there */

  /* Now take care of adding in the rest of the parameter */

  counter = 1;
  while ((str[counter]) && (str[counter] != '/')) counter++;

  strcat(retval, (str + counter));

  return retval;
}

/* freeargs will free up the memory that was dynamically allocated for the
   array */

void freeargs(char *args[])
{
  int counter = 0;

  while (args[counter]) {
    free(args[counter]);
    counter++;
  }
}

/* Calculates number of arguments in args */

void calcargc(char *args[], int *argc)
{
  *argc = 0;
  while (args[*argc]) {
    (*argc)++;            /* Increment while non-null */
  }
  (*argc)--;            /* Decrement after finding a null */
}

/* parseredir will see if it can find a redirection operator oper
   in the array args[], and, if so, it will return the parameter (filename)
   to that operator. */

char *parseredir(char oper, char *args[])
{
  int counter;
  int argc;
  static char retval[MAXINPUTLINE];

  calcargc(args, &argc);

  for (counter = argc; counter >= 0; counter--) {
    fflush(stderr);
    if (args[counter][0] == oper) {
      if (args[counter][1]) {    /* Filename specified without a space */
       strcpy(retval, args[counter] + 1);
       argsdelete(args + counter);
       return retval;
      } else {            /* Space seperates oper from filename */
       if (!args[counter+1]) {    /* Missing filename */
         fprintf(stderr, "Error: operator %c without filename", oper);
         exit(255);
       }
       strcpy(retval, args[counter+1]);
       argsdelete(args + counter + 1);
       argsdelete(args + counter);
       return retval;    
      }
    }
  }
  return NULL;            /* No match */
}

/* Argsdelete will remove a string from the array */

void argsdelete(char *args[])
{
  int counter = 0;
  if (!args[counter]) return;    /* Empty argument list: do nothing */
  free(args[counter]);
  while (args[counter]) {
    args[counter] = args[counter + 1];
    counter++;
  }
}

编辑:好的,所以我明白了。解析(输入)和stripcrlf(输入)都搞乱了。如何,我不确定,但这不是重点。我最终需要做的是不仅打开文件并保持打开状态,直到它在程序结束前关闭。我必须这样做:fopen(&#34; .simpleshell_history&#34;,&#34; a&#34;); fprintf(文件,&#34;%d&#34;,count); fprintf(文件,&#34;%s \ n&#34;,输入); FCLOSE(文件);基本上,我必须打开文件,把东西放进去,然后在解析器之前关闭它,这样可能会有效果。输出现在很完美。谢谢大家的帮助。

1 个答案:

答案 0 :(得分:1)

我从问题代码中删除了不相关的行:

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

#define MAXINPUTLINE 100

int main(void) {
   char input[MAXINPUTLINE];
   int count = 2;
// start=NULL;
   char data[MAXINPUTLINE];
   FILE *file;

我将文件名从“.simpleshell_history”修改为“simpleshell_history”,因此文件未被隐藏(出于测试目的)。

   file = fopen("simpleshell_history", "w");  
// char *history="history";
// char *last="!!";
// signal_c_init();

   printf("Welcome to the sample shell!  You may enter commands here, one\n");
   printf("per line.  When you're finished, press Ctrl+D on a line by\n");
   printf("itself.  I understand basic commands and arguments separated by\n");
   printf("spaces, redirection with < and >, up to two commands joined\n");
   printf("by a pipe, tilde expansion, and background commands with &.\n\n");

   printf("\nclsh$ ");

   fgets(data, sizeof(data), stdin);
// create(data);
   fprintf(file, "%d ", 1);
   fprintf(file, "%s", data);
// parse(data);

   printf("\nclsh$ ");

   while (fgets(input, sizeof(input), stdin)) {
//    stripcrlf(input);
//    parse(input);

//    insert_end(input);
      fprintf(file, "%d ", count);
      fprintf(file, "%s", input);

我添加了以下两行,以便摆脱循环。

      if(0 == strncmp("quit", input, 4))
         break;

      count++;
      printf("\nclsh$ ");
   }

   fclose(file);
   return 0;
}

我运行了程序:

> ./test
Welcome to the sample shell!  You may enter commands here, one
per line.  When you're finished, press Ctrl+D on a line by
itself.  I understand basic commands and arguments separated by
spaces, redirection with < and >, up to two commands joined
by a pipe, tilde expansion, and background commands with &.


clsh$ hello

clsh$ there

clsh$ QUIT

clsh$ QUIT

clsh$ quit

文件“simpleshell_history”的内容:

1 hello
2 there
3 QUIT
4 QUIT
5 quit

代码似乎按预期执行,没有意外的“输出文件内容重复”。