簡體   English   中英

在C中實現shell並需要幫助處理輸入/輸出重定向

[英]Implementing shell in C and need help handling input/output redirection

第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輪

我已經在C中使用了一個相對簡單的shell一段時間了,但我在實現input(<)和output(>)重定向時遇到了麻煩。 幫我找到以下代碼中的問題:

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);
}

我可能在那里有一些不必要的材料,因為我一直在嘗試不同的東西來讓它發揮作用。 我不確定出了什么問題。

重定向后,您有太多文件描述符打開。 讓我們剖析這兩段:

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);
}

我將成為慈善機構並忽略你忽略錯誤這一事實。 但是,您需要錯誤檢查系統調用。

在第一段中,您打開一個文件並在變量fd捕獲文件描述符(可能是3)。 然后,您通過標准輸入( STDIN_FILENO )復制文件描述符。 但請注意,文件描述符3仍處於打開狀態。 然后你做一個dup(0) (為了保持一致,應該是STDIN_FILENO ),得到另一個文件描述符,也許是4.所以你有文件描述符0,3和4指向同一個文件(事實上,同樣的開放文件描述 - 注意打開的文件描述與打開的文件描述符不同)。 如果你對current_in的意圖是保留(父)shell的標准輸入,你必須在執行覆蓋輸出的dup2()之前執行dup() 但是,最好不要改變父shell的文件描述符; 它比重新復制文件描述符的開銷更少。

然后你或多或少地重復第二段中的過程,首先用fd = creat(...)調用覆蓋文件描述符3的唯一記錄但是獲得一個新的描述符,可能是5,然后通過標准輸出復制它。 然后你做一個dup(1) ,產生另一個文件描述符,也許是6。

因此,您將主shell的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...
}

在執行任何此操作之前,您應該確保已經刷新了shell的標准I / O通道,可能是使用fflush(0) ,這樣如果分叉的子進程由於問題而寫入標准錯誤,則沒有無關的重復輸出。

另請注意,應對錯誤檢查各種open()調用。

重定向后,您有太多文件描述符打開。 你需要的代碼就是這個。

    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) 
    }
}

這是正在發生的事情。 在調用fork() ,有兩個進程正在執行,這些進程是原始進程的重復。 不同之處在於fork()的返回值,它存儲在pid

然后,兩個進程(shell和子進程)將stdin和stdout重定向到相同的文件。 我認為你試圖在current_out保存以前的fd,但正如Seth Robertson指出的那樣,這當前不起作用,因為正在保存錯誤的文件描述符。 父母也恢復了它的標准輸出,但不是標准輸入。

你可以修復這個bug,但你可以做得更好。 你實際上不必重定向父輸出,只是孩子的輸出。 所以先簡單檢查一下pid 然后也沒有必要恢復任何文件描述符。

暫無
暫無

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

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