C++ Myshell分段错误,获取输入可能存在问题?

C++ Myshell分段错误,获取输入可能存在问题?,c++,shell,C++,Shell,我从事这个项目已经有一段时间了。其目的是制作一个功能强大的shell,它可以执行几乎所有的shell命令(除了cd)。它几乎做了我想让它做的一切,除了几件事。第一个是,当我放置一个“&”来表示后台处理时,它会这样做,但不会打印另一行myshell>。我仍然可以输入一些东西,但是myshell>永远不会显示,无论我在哪里放置另一个cout来返回,它都会打印一个myshell>,但在下一次按enter键时会出现seg错误。如果我没有解释清楚,我很抱歉,但这真的让我发疯了。如果你有任何建议,请告诉我

我从事这个项目已经有一段时间了。其目的是制作一个功能强大的shell,它可以执行几乎所有的shell命令(除了cd)。它几乎做了我想让它做的一切,除了几件事。第一个是,当我放置一个“&”来表示后台处理时,它会这样做,但不会打印另一行myshell>。我仍然可以输入一些东西,但是myshell>永远不会显示,无论我在哪里放置另一个cout来返回,它都会打印一个myshell>,但在下一次按enter键时会出现seg错误。如果我没有解释清楚,我很抱歉,但这真的让我发疯了。如果你有任何建议,请告诉我

#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <cstdio>
#include <sys/wait.h>
#include <stdio.h>

/*Function that parses the command the user inputs.
  It takes myArgv and myString as inputs.
  It returns the value of exitcond, which is used to see if the user wants to exit or not.
  Also, this is where myString is tokenized using strok()*/
int parseCommand(char *myArgv[10], char myString[255])
{
    int exitcond=0;
    if((strcmp(myArgv[0], "exit") == 0)||(strcmp(myArgv[0], "quit")==0))
    {
        exitcond = 1;
        return exitcond;
    }
    int i;
    char *token;
    token = strtok(myString," ");
        i=0;
        while (token != NULL)
        {
            myArgv[i] = token;
            token = strtok(NULL," ");
            i++;
        }

        /*
         * Set the last entry our new argv to a null char
         * (see man execvp to understand why).
         */
        myArgv[i] = '\0';
    return exitcond;
}
/*Function that gets the command from the user and sees if they want 
background processing or not (presence of '&').
  It takes inputs of choose and myString. choose is the variable for 
whether background processing is necessary or not, while myString is 
an empty character array.
  It outputs the value of the choose variable for lter use.*/
int getCommand(int choose, char myString[255])
{
    int i;
    choose=0;
    fgets(myString, 256, stdin);
    if (myString[0]=='\0')
    {
        choose=0;
        return choose;
    }
        for (i=0; myString[i]; i++)
        {
            if (myString[i]== '&')
            {
            choose=1;
            myString[i]=' ';
        }

        if (myString[i] == '\n')
        {
            myString[i] = '\0';
        }
    }
    return choose;
}
/*Main function where all the calling of other functions and processes 
is done. This is where the user enters and exits the shell also. All 
usage of fork, pid, waitpid and execvp is done here.*/
int main() 
{
    using namespace std;
    int exitCondition=0, i=0, status;
    char myString[255];
    char *token, *myArgv[10];
    pid_t pid, waiting;
    int bg=0;
    while (!exitCondition)
    {
        /* print a prompt and allow the user to enter a stream of characters */
        cout << "myshell> ";
        bg=0;
        int choose=0;
        bg=getCommand(choose,myString);
        exitCondition=parseCommand(myArgv,myString);
        if(exitCondition==1)
        {
            cout<<"Thank you for using my shell.\n";
        }

    else {
     /*   while (myString[0]=='\0')
        {
            cout<<"myshell> ";
            bg=getCommand(choose,myString);
        }*/
        /* The user has a command, so spawn it in a child process */

        pid = fork();

        if (pid == -1)
        {
            /* to understand why this is here, see man 2 fork */
            cout << "A problem arose, the shell failed to spawn a child process" << endl;
            return(1);
        }

        else if (pid == 0)
        {
            // Child process 
            execvp(myArgv[0],myArgv);
            cout << "Bad command or file name, please try again!\n" << endl;
            return 0;
        } else {
            /* This makes sure that the spawned process is run in the foreground, 
               because the user did not choose background */
            if(bg==0)
            {
                waitpid(pid,NULL,0);

            }
          }

    }
}
return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
/*解析用户输入的命令的函数。
它将myArgv和myString作为输入。
它返回exitcond的值,该值用于查看用户是否要退出。
此外,这也是使用strok()标记myString的地方*/
int parseCommand(char*myArgv[10],char myString[255])
{
int exitcond=0;
if((strcmp(myArgv[0],“退出”)==0)| |(strcmp(myArgv[0],“退出”)==0))
{
exitcond=1;
返回exitcond;
}
int i;
字符*令牌;
token=strtok(myString,“”);
i=0;
while(令牌!=NULL)
{
myArgv[i]=令牌;
令牌=strtok(空,“”);
i++;
}
/*
*将新argv的最后一个条目设置为空字符
*(参见man execvp了解原因)。
*/
myArgv[i]='\0';
返回exitcond;
}
/*函数,该函数从用户处获取命令并查看用户是否需要
是否进行后台处理(是否存在“&”)。
它接受choose和myString的输入。choose是的变量
是否需要后台处理,而myString是
空字符数组。
它输出choose变量的值以供lter使用*/
int-getCommand(int-choose,char-myString[255])
{
int i;
选择=0;
fgets(myString,256,stdin);
如果(myString[0]='\0')
{
选择=0;
返回选择;
}
for(i=0;myString[i];i++)
{
if(myString[i]=='&')
{
选择=1;
myString[i]='';
}
if(myString[i]='\n')
{
myString[i]='\0';
}
}
返回选择;
}
/*主函数,其中所有其他函数和进程的调用
完成了。这也是用户进入和退出shell的地方。全部的
fork、pid、waitpid和execvp的使用在这里完成*/
int main()
{
使用名称空间std;
int exitCondition=0,i=0,状态;
char myString[255];
char*token,*myArgv[10];
pid_t pid,等待;
int-bg=0;
而(!exitCondition)
{
/*打印提示并允许用户输入字符流*/

cout好的,您有三个bug,其中一个导致segfault。在其他bug中,一个会在传递给
execvp
的数组中放入垃圾参数,另一个会泄漏后台作业的僵尸进程

我已经更正了代码并注释了错误所在的位置和修复程序[请原谅这种无缘无故的风格清理]:

#include <cstdio>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define AVCOUNT     100
#define STRBUFLEN   2000

/*Function that parses the command the user inputs.
  It takes myArgv and myString as inputs.
  It returns the value of exitcond, which is used to see if the user wants to
  exit or not.
  Also, this is where myString is tokenized using strok()*/
int
parseCommand(char **myArgv, char *myString)
{
    char *token;
    char *bp;
    int exitcond = 0;
    int i;

    // NOTE/BUG: original check for exit/quit was here -- at this point
    // myArgv is undefined (hence the segfault)

    // NOTE/BUG: your original loop -- at the end i was one beyond where it
    // should have been so that when myArgv gets passed to execvp it would
    // have an undefined value at the end
#if 0
    token = strtok(myString, " ");
    i = 0;
    while (token != NULL) {
        myArgv[i] = token;
        token = strtok(NULL, " ");
        i++;
    }
#endif

    // NOTE/BUGFIX: here is the corrected loop
    i = 0;
    bp = myString;
    while (1) {
        token = strtok(bp, " ");
        bp = NULL;

        if (token == NULL)
            break;

        myArgv[i++] = token;
    }

    /*
     * Set the last entry our new argv to a null pointer
     * (see man execvp to understand why).
     */
    // NOTE/BUG: with your code, i was one too high here
    myArgv[i] = NULL;

    // NOTE/BUGFIX: moved exit/quit check to here now that myArgv is valid
    token = myArgv[0];
    if (token != NULL) {
        if ((strcmp(token, "exit") == 0) || (strcmp(token, "quit") == 0))
            exitcond = 1;
    }

    return exitcond;
}

/*Function that gets the command from the user and sees if they want
  background processing or not (presence of '&').
  It takes inputs of choose and myString. choose is the variable for
  whether background processing is necessary or not, while myString is
  an empty character array.
  It outputs the value of the choose variable for lter use.*/
int
getCommand(int choose, char *myString)
{
    int i;

    choose = 0;
    fgets(myString, STRBUFLEN, stdin);

    if (myString[0] == '\0') {
        choose = 0;
        return choose;
    }

    for (i = 0; myString[i]; i++) {
        if (myString[i] == '&') {
            choose = 1;
            myString[i] = ' ';
        }

        if (myString[i] == '\n') {
            myString[i] = '\0';
            break;
        }
    }

    return choose;
}

/*Main function where all the calling of other functions and processes
  is done. This is where the user enters and exits the shell also. All
  usage of fork, pid, waitpid and execvp is done here.*/
int
main()
{
    using namespace std;
    int exitCondition = 0;
    int status;
    char myString[STRBUFLEN];
    char *myArgv[AVCOUNT];
    pid_t pid;
    int bg = 0;

    while (!exitCondition) {
        // NOTE/BUGFIX: without this, any background process that completed
        // would become a zombie because it was never waited for [again]
        // reap any finished background jobs
        while (1) {
            pid = waitpid(0,&status,WNOHANG);
            if (pid < 0)
                break;
        }

        /* print a prompt and allow the user to enter a stream of characters */
        cout << "myshell> ";
        bg = 0;
        int choose = 0;

        bg = getCommand(choose, myString);

        exitCondition = parseCommand(myArgv, myString);
        if (exitCondition == 1) {
            cout << "Thank you for using my shell.\n";
            break;
        }

        /* while (myString[0]=='\0') { cout<<"myshell> "; bg=getCommand(choose,myString); } */
        /* The user has a command, so spawn it in a child process */

        pid = fork();

        if (pid == -1) {
            /* to understand why this is here, see man 2 fork */
            cout << "A problem arose, the shell failed to spawn a child process" << endl;
            return 1;
        }

        if (pid == 0) {
            // Child process
            execvp(myArgv[0], myArgv);
            cout << "Bad command or file name, please try again!\n" << endl;
            return 1;
        }

        /* This makes sure that the spawned process is run in the
        foreground, because the user did not choose background */
        if (bg == 0)
            waitpid(pid, &status, 0);
    }

    return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义AVCOUNT 100
#定义STRBUFLEN 2000
/*解析用户输入的命令的函数。
它将myArgv和myString作为输入。
它返回exitcond的值,该值用于查看用户是否希望
是否退出。
此外,这也是使用strok()标记myString的地方*/
int
parseCommand(char**myArgv,char*myString)
{
字符*令牌;
char*bp;
int exitcond=0;
int i;
//注意/错误:退出/退出的原始检查就在这里——此时
//myArgv未定义(因此存在SEGFULT)
//注意/错误:你最初的循环——在最后我是一个超出它的地方
//应该是这样的,当myArgv传递给execvp时
//在末尾有一个未定义的值
#如果0
token=strtok(myString,“”);
i=0;
while(令牌!=NULL){
myArgv[i]=令牌;
令牌=strtok(空,“”);
i++;
}
#恩迪夫
//注意/错误修复:这是正确的循环
i=0;
bp=myString;
而(1){
令牌=strtok(bp,“”);
bp=NULL;
if(标记==NULL)
打破
myArgv[i++]=令牌;
}
/*
*将新argv的最后一个条目设置为空指针
*(参见man execvp了解原因)。
*/
//注意/错误:对于你的代码,我在这里是一个太高了
myArgv[i]=NULL;
//注意/错误修复:现在myArgv有效,将退出/退出检查移到此处
token=myArgv[0];
if(令牌!=NULL){
if((strcmp(令牌,“退出”)==0)| |(strcmp(令牌,“退出”)==0))
exitcond=1;
}
返回exitcond;
}
/*函数,该函数从用户处获取命令并查看用户是否需要
是否进行后台处理(是否存在“&”)。
它接受choose和myString的输入
是否需要后台处理,而myString是
空字符数组。
它输出choose变量的值以供lter使用*/
int
getCommand(int-choose,char*myString)
{
int i;
选择=0;
fgets(myString、STRBUFLEN、stdin);
如果(myString[0]='\0'){
选择=0;
返回选择;
}
for(i=0;myString[i];i++){
if(myString[i]=='&'){
选择=1;
myString[i]='';
}
if(myString[i]='\n'){
myString[i]='\0';
打破
}
}
返回选择;
}
/*主函数,其中所有其他函数和进程的调用
完成。这也是用户进入和退出shell的地方。所有
fork、pid、waitpid和execvp的使用在这里完成*/
int
main()
{
使用名称空间std;
在里面