簡體   English   中英

為什么此程序輸出的行重復?

[英]Why are lines, output by this program, duplicated?

好的,因此,我目前正在致力於實現簡單外殼歷史記錄。 要求的一部分是將所有命令輸出到一個文件中,每行對它們進行編號。

目前,我的所有其他功能都運行良好,但是文件的輸出給了我一個非常具體的問題。 它正在保存命令並將它們放入編號文件中,但是每個命令給我大約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++;
  }
}

編輯:好的,所以我明白了。 parse(input)和stripcrlf(input)搞砸了。 如何確定,我不確定,但這不重要。 最后,我需要做的不僅是打開文件並保持打開狀態,直到在程序結束之前將其關閉。 我必須這樣做:fopen(“。simpleshell_history”,“ a”); fprintf(file,“%d”,count); fprintf(file,“%s \\ n”,輸入); FCLOSE(文件); 基本上,我必須打開文件,將文件放入,然后在解析器之前將其關閉,這樣可能會起作用。 現在的輸出是完美的。 感謝大家的幫助。

我從問題代碼中刪除了不相關的行:

#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

該代碼似乎按預期執行,沒有意外的“輸出文件內容重復”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM