为什么 getline() 获取前一行?

Why does getline() get the preceding line?

bdoetsch@Kaladin:~/Documents/School$ ./shell
Shell(pid = 6955) 1> ls
command: ls
argv[i] = ls
argv[i] = ./shell
Parent says 'child process has been forked with pid=6956'
./shell
Parent says 'wait() returned so the child with pid=-1 is finished'
Shell(pid = 6955) 2> 

这是我正在做的作业,但我有点难过。

嗨,我正在尝试编写一个 shell 程序,但我不明白为什么 getline 会获取前面的输入行。

此外,该程序将执行“ls -al”、“pwd”和其他一些命令。但不仅仅是 ls.

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

int tokenizer(char *str,char **argv);
void doCommand(char **argv);

int main()
{
    pid_t pid;
    char *command;
    int Num_bytes_read;
    size_t nbytes = 60;
    int NumCommand = 0;
    char **argv;

    int NumOfArgs;

    command = (char *) malloc (nbytes + 1);
    command = NULL;
    while(1)
    {   
        NumCommand++;
        printf("Shell(pid = %d) %d> ",getpid(),NumCommand);

        fflush(stdin);

        Num_bytes_read = getline(&command,&nbytes,stdin);

        printf("command: %s",command);        
        if(Num_bytes_read == -1)
        {
            printf("\n ERRor\n");
        }
        else
        {  
            int x = tokenizer(command,argv);
            int i;        

            for (i = 0; i < (x+1); ++i)
                printf ("argv[i] = %s\n",argv[i]);
            //if (strcmp(argv[0], "quit")){
            //break;
            //}
            doCommand(argv);
        }
    }
    return 0;
}

int tokenizer(char *str, char **argv)
{
    //const char s[2] = " "; 
    char ** res  = NULL;
    char * p    = strtok (str, " \n");
    int n_spaces = 0, i;
    while (p) 
    {
        res = realloc (res, sizeof (char*) * ++n_spaces);
        if (res == NULL)
            exit (-1); /* memory allocation failed */
        res[n_spaces-1] = p;
        argv[n_spaces-1]= res[n_spaces-1];

        p = strtok (NULL, " \n");
    }
    // realloc one extra element for the last NULL 

    res = realloc (res, sizeof (char*) * (n_spaces+1));
    res[n_spaces] = 0;

    argv = res;
    return n_spaces;
}      

void doCommand(char **argv)
{
    pid_t  pid;
    pid_t cpid; /* Pid of child to be returned by wait. */
    int fd[2];  // dual pipeline
    int status; /* Exit status of child. */

    int nbytes;
    int commandStatus;

    pipe(fd);

    pid = fork();   // Preceding with fork]
    if (pid < 0) 
    {   
        printf("forking child process failed\n");
        exit(1);
    }
    else if (pid == 0)     // fork for the child
    {    
        close(fd[0]); // close up reader side of pipe

        cpid = getpid();  

        /* Send "string" through the output side of pipe */  
        write(fd[1], &cpid,sizeof(cpid));
        //argv[0] = "ls";
        //argv[1] = NULL;
        commandStatus = execvp(*argv, argv);
        if (commandStatus < 0)  /* execute the command  */
        {    
            printf("Try again, command failed\n");
            exit(1);
        }
    }
    else if (pid > 1)                // fork for the parent
    {  
        close(fd[1]);

        /* Read in the child pid from the pipe */
        nbytes = read(fd[0], &cpid , sizeof(cpid));
        printf("Parent says 'child process has been forked with pid=%ld'\n",(long)cpid);
        wait(NULL);
        cpid = wait(&status);      /* wait for completion  */

        printf("Parent says 'wait() returned so the child with pid=%ld is finished'\n",(long)cpid);
    }
}

问题是 argv 没有真正初始化,因为你改变的是 tokenizer() 中的局部指针,而不是 main() 中的指针。

你要做的就是传递argv的地址而不是指针本身,然后像这样改变tokenizer()

int tokenizer(char *str, char ***argv)
{
    char ** res      = NULL;
    char *  p        = NULL;
    int     n_spaces = 0;

    if ((str == NULL) || (argv == NULL))
        return 0;

    p     = strtok (str, " \n"); 
    *argv = NULL;
    while (p)
    {
        res = realloc (*argv, sizeof(char *) * ++n_spaces);
        if (res == NULL)
        {
            free(*argv);
            return 0;
        }
        res[n_spaces - 1] = p;
        *argv             = res;
        p                 = strtok (NULL, " \n");
    }
    res = realloc (*argv, sizeof(char *) * (n_spaces + 1));
    if (res == NULL)
    {
        free(*argv);
        return 0;
    }
    res[n_spaces] = 0;
    *argv         = res;

    return n_spaces;
}

在主要部分

int x = tokenizer(command, &argv);
/*                         ^ address of argv */

你也从来没有检查过最后一个 realloc,所以我修复了一些我认为你做的有点不安全的事情。

exit() on malloc/realloc 失败在你的情况下并不是真正必要的,你可以只 free() 已经分配的指针和 return 0,然后在 main().

中处理

此外,我建议在有意义的地方使用 const 限定词,例如

void doCommand(const char *const *argv)

另外,不要 fflush() stdin 这是未定义的行为。