用C实现外壳和需要帮助处理输入/输出重定向(Implementing shell in C and

2019-06-26 08:40发布

第2轮

读一些答案后,我修改后的代码:

int pid = fork();

if (pid == -1) {
    perror("fork");
} else if (pid == 0) {   

    if (in) { //if '<' char was found in string inputted by user
        int fd0 = open(input, O_RDONLY, 0);
        dup2(fd0, STDIN_FILENO);
        close(fd0);
        in = 0;
    }

    if (out) { //if '>' was found in string inputted by user
        int fd1 = creat(output, 0644);
        dup2(fd1, STDOUT_FILENO);
        close(fd1);
        out = 0;
    }   

    execvp(res[0], res);
    perror("execvp");
    _exit(1);
} else {
    waitpid(pid, 0, 0);
    free(res);
}

它的工作原理,但似乎标准输出没有被重新连接或诸如此类的话。 下面是执行:

SHELL$ cat > file
hello, world
this is a test
SHELL$ cat < file //no output
SHELL$ ls //no output

“<”和“>”两者工作,但在它们被执行没有输出。


第1轮

我已经工作在下一个而相对简单壳现在,但我有实施麻烦输入(<)和输出(>)重定向。 帮我看看下面的代码的问题:

int fd;
int pid = fork();
int current_out;

if (in) { //if '<' char was found in string inputted by user
    fd = open(input, O_RDONLY, 0);
    dup2(fd, STDIN_FILENO);
    in = 0;
    current_out = dup(0);
}

if (out) { //if '>' was found in string inputted by user
    fd = creat(output, 0644);
    dup2(fd, STDOUT_FILENO);
    out = 0;
    current_out = dup(1);
}

if (pid == -1) {
    perror("fork");
} else if (pid == 0) {       
    execvp(res[0], res);
    perror("execvp");
    _exit(1);
} else {
    waitpid(pid, 0, 0);
    dup2(current_out, 1);
    free(res);
}

我可能有一些不必要的材料在那里,因为我一直在尝试不同的东西来得到它的工作。 我不知道什么错误。

Answer 1:

你有太多的文件描述符的重定向后开放。 让我们来剖析两段:

if (in) { //if '<' char was found in string inputted by user
    fd = open(input, O_RDONLY, 0);
    dup2(fd, STDIN_FILENO);
    in = 0;
    current_in = dup(0);  // Fix for symmetry with second paragraph
}

if (out) { //if '>' was found in string inputted by user
    fd = creat(output, 0644);
    dup2(fd, STDOUT_FILENO);
    out = 0;
    current_out = dup(1);
}

我会是慈善,而忽略你是忽略错误的情况。 但是,你需要错误检查您的系统调用。

在第一段中,你打开一个文件,并捕获文件描述符(它很可能是3)在变量fd 。 然后,重复以上标准输入(文件描述符STDIN_FILENO )。 但是请注意,该文件描述3仍处于打开状态。 然后,你做一个dup(0)其中,为了保持一致性,应STDIN_FILENO ),获得另一个文件描述符,也许4.所以,你有文件描述符0,3和同一文件(实际上4指点下,相同的开放文件描述 - 指出的是一个打开的文件的描述是从一个打开文件描述符不同)。 如果您打算current_in是保留(父)shell的标准输入,你必须这样做dup()你做之前dup2()覆写输出。 但是,你会过不改变父shell的文件描述符更好; 它是不是重新复制文件描述符的开销少。

然后,你或多或少地重复该过程在第二段,首先覆盖文件描述符的唯一记录3是与开放fd = creat(...)电话,但获得新的描述符,也许5,然后复制,超过标准输出。 然后,做一个dup(1)产生另一个文件描述符,或许6。

所以,你必须重定向到文件(没有复原那些原始值的方式)的主要外壳的stdin和stdout。 你的第一个问题,因此,是你正在做的重定向你面前fork() ; 你应该后做fork() -虽然当你在进程之间的管道,你需要分叉之前创建的管道。

你的第二个问题是,你需要关闭文件描述符,其中一个你不再有一个参考的太多了。

所以,你可能需要:

if ((pid = fork()) < 0)
    ...error...
else if (pid == 0)
{
    /* Be childish */
    if (in)
    {
        int fd0 = open(input, O_RDONLY);
        dup2(fd0, STDIN_FILENO);
        close(fd0);
    }

    if (out)
    {
        int fd1 = creat(output , 0644) ;
        dup2(fd1, STDOUT_FILENO);
        close(fd1);
    }
    ...now the child has stdin coming from the input file, 
    ...stdout going to the output file, and no extra files open.
    ...it is safe to execute the command to be executed.
    execve(cmd[0], cmd, env);   // Or your preferred alternative
    fprintf(stderr, "Failed to exec %s\n", cmd[0]);
    exit(1);
}
else
{
    /* Be parental */
    ...wait for child to die, etc...
}

你做任何在此之前,你应该确保你已经刷新了壳的标准I / O通道,可能通过使用fflush(0)因此,如果派生的子写入标准错误,因为一个问题,有没有外来的双重输出。

还要注意的是不同open()调用应该是错误检查。



Answer 2:

你有太多的文件描述符的重定向后开放。 你需要的代码是这样的。

    if (pid == 0)
{          /* for the child process:         */

    // function for redirection ( '<' , '>' )

    int fd0,fd1,i,in=0,out=0;
    char input[64],output[64];

    // finds where '<' or '>' occurs and make that argv[i] = NULL , to ensure that command wont't read that

    for(i=0;argv[i]!='\0';i++)
    {
        if(strcmp(argv[i],"<")==0)
        {        
            argv[i]=NULL;
            strcpy(input,argv[i+1]);
            in=2;           
        }               

        if(strcmp(argv[i],">")==0)
        {      
            argv[i]=NULL;
            strcpy(output,argv[i+1]);
            out=2;
        }         
    }

    //if '<' char was found in string inputted by user
    if(in)
    {   

        // fdo is file-descriptor
        int fd0;
        if ((fd0 = open(input, O_RDONLY, 0)) < 0) {
            perror("Couldn't open input file");
            exit(0);
        }           
        // dup2() copies content of fdo in input of preceeding file
        dup2(fd0, 0); // STDIN_FILENO here can be replaced by 0 

        close(fd0); // necessary
    }

    //if '>' char was found in string inputted by user 
    if (out)
    {

        int fd1 ;
        if ((fd1 = creat(output , 0644)) < 0) {
            perror("Couldn't open the output file");
            exit(0);
        }           

        dup2(fd1, STDOUT_FILENO); // 1 here can be replaced by STDOUT_FILENO
        close(fd1);
    }

    execvp(*argv, argv);
    perror("execvp");
    _exit(1);

    // another syntax
    /*      if (!(execvp(*argv, argv) >= 0)) {     // execute the command  
            printf("*** ERROR: exec failed\n");
            exit(1);
     */ 
}


    else if((pid) < 0)
    {     
        printf("fork() failed!\n");
        exit(1);
    }

    else {                                  /* for the parent:      */

        while (!(wait(&status) == pid)) ; // good coding to avoid race_conditions(errors) 
    }
}


Answer 3:

这里是发生了什么。 在调用fork()有两个进程执行是原工艺的副本。 所不同的是在的返回值fork()存储在pid

然后两个进程(外壳和孩子)重定向的stdin和stdout相同的文件。 我认为你试图保存在以前FD current_out ,但赛斯罗伯逊所指出的,这并不目前的工作,因为错误的文件描述符被保存。 父也恢复其标准输出,而不是标准输入。

你可以修复这个bug,但你可以做的更好。 你实际上并不需要重定向父母的输出,只是孩子的。 所以,简单地检查pid第一。 再有就是也没有必要恢复任何文件描述符。



文章来源: Implementing shell in C and need help handling input/output redirection
标签: c linux shell