【问题标题】:segmentation fault in a simplistic shell简单外壳中的分段错误
【发布时间】:2023-10-25 05:45:01
【问题描述】:

我正在为一个简单的 c shell 编写代码。它存储最后 10 个命令的历史记录。如果“r”作为命令输入,那么它应该运行历史记录中最近的命令。此外,如果输入了“r x”,其中“x”是历史记录中要执行的命令的第一个字母,那么它将运行以该字母开头的最新命令。

我遇到了从历史记录复制到 inputBuffer 的问题;当我尝试这样做时,它会出现分段错误。

这是代码(这很乱,我一直在尝试很多不同的东西,希望能修复它)。 任何建议将不胜感激!

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


#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
#define BUFFER_SIZE 50

#define buffer "Command History:\n"

char history[10][MAX_LINE];
int count = 0;
int caught = 0;
int ct =0;
int rFlag =0;

/**
* setup() reads in the next command line, separating it into distinct tokens
 * using whitespace as delimiters. setup() sets the args parameter as a 
 * null-terminated string.
 */

void shell(void);

void printHistory()
{
      int i;
  int j = 0;
  int hcount = count;

      for (i = 0; i<10;i++)
    {
      printf("%d. ", hcount);
      while (history[i][j] != '\n' && history[i][j] != '\0'){
      printf("%c", history[i][j]);
      j++;
    }
   printf("\n");
   j = 0;   

   hcount--;
   if (hcount ==  0)
    break;
   }


}   

/* the signal handler function */
void handle_SIGINT() 
{
  write(STDOUT_FILENO,buffer,sizeof(buffer));
  printHistory();
  printf("\nCOMMAND->");
  caught = 1;
}


void setup(char inputBuffer[], char *args[],int *background)
{
  int k;
  int length, /* # of characters in the command line */
    i,     /* loop index for accessing inputBuffer array */
    start;  /* index where beginning of next command parameter is */
  //ct     /* index of where to place the next parameter into args[] */

  ct = 0;

  printf("Setup");

  /* read what the user enters on the command line */
  if (rFlag!=1)
    {
  length = read(STDIN_FILENO, inputBuffer, MAX_LINE); 
  rFlag = 0;
    }
   printf("buff=%i", strlen(inputBuffer));
  if (caught == 1)
{
  length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
  caught = 0;
}

  if ((strcmp(inputBuffer,"r\n\0")!=0) && (strncmp(inputBuffer, "r x", 2)!=0))
    {
      for (i = 9;i>0; i--)
    {
      strcpy(history[i], history[i-1]);
    }
      strcpy(history[0],inputBuffer); //this works
      count++;
    }


start = -1;
 if (length == 0)
  exit(0);            /* ^d was entered, end of user command stream */
if (length < 0){ 
  perror("error reading the command\n");
  exit(-1);           /* terminate with error code of -1 */
}


  /* examine every character in the inputBuffer */

       printf("beforeCase");
for (i=0;i<length;i++) 
  {
switch (inputBuffer[i])
  {
  case ' ':
  case '\t' :               /* argument separators */
    if(start != -1)
      {
    args[ct] = &inputBuffer[start];    /* set up pointer */
    ct++;
      }
    inputBuffer[i] = '\0'; /* add a null char; make a C string */
    start = -1;
    break;

  case '\n':                 /* should be the final char examined */
    if (start != -1){
      args[ct] = &inputBuffer[start];     
      ct++;
    }
    inputBuffer[i] = '\0';
    args[ct] = NULL; /* no more arguments to this command */
    break;  

  default :             /* some other character */
    if (start == -1)
      start = i;
    if (inputBuffer[i] == '&'){
      *background  = 1;
      inputBuffer[i] = '\0';
    }
  } 
  }    

args[ct] = NULL; /* just in case the input line was > 80 */

}

int main(void)
{
  char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
  int background;             /* equals 1 if a command is followed by '&' */
  char *args[MAX_LINE/+1];/* command line (of 80) has max of 40 arguments */
  pid_t pid;
  char iBuffer2[MAX_LINE];
  int i;

  /* set up the signal handler */
  struct sigaction handler;
  handler.sa_handler = handle_SIGINT; 
  sigaction(SIGINT, &handler, NULL);
  while (1){            /* Program terminates normally inside setup */
   background = 0;
    printf("COMMAND->");
   fflush(0);
   setup(inputBuffer, args, &background);/* get next command */

   if ((strcmp(args[0],"r")==0) && (ct==1))
   {
    fflush(0);
  rFlag = 1;
  background = 0;
  printf("strlen=%i", strlen(history[0]));
  setup(history[0], args, &background);
  printf("AFTER");

}

if (strcmp(args[0],"exit")==0)break; //exits if args[0]=="exit"
pid_t pid = fork();
if (pid < 0) { //Error Occured
  fprintf(stderr, "Fork Failed");
  exit(-1);
}
else if (pid == 0) {//Child Process
  execvp(args[0], args); // Execute command in args
  printf("%s: Command not found.\n", args[0]); //If invalid command
  exit(0);
}
  else { // Parent Process
if (background == 0) { // case with No '&'
  wait(NULL);
}
  }
printf("\nFinish\n");
}
}

【问题讨论】:

  • 你有没有尝试使用 GDB?
  • 你试过缩进代码吗?

标签: c shell history handler sigint


【解决方案1】:

catchsegv 可用于在程序因分段错误而终止时创建堆栈跟踪。 http://www.linuxfromscratch.org/lfs/view/stable/chapter06/glibc.html

【讨论】:

    【解决方案2】:

    如果您要使用低级 i/o,那么您有责任在执行 read() 之后 NUL 终止数据,然后再对读取的数据执行任何字符串操作。

    length = read(STDIN_FILENO, inputBuffer, MAX_LINE-1);
    if (length > 0)
       inputBuffer[length] = '\0';
    else
       inputBuffer[0] = '\0';
    

    但 read() 可能不是您一开始就想使用的,因为您似乎希望一次得到一行。尝试改用 fgets()。

    【讨论】: