0

我已经完成了 Unix shell 的实践实现,除了当它的输出是文件时我在实现时遇到问题;catIE: cat foo.txt > bar.txt-foo将 的内容输出到bar.

让我们从主函数开始,然后我将定义子方法:

int main(int argc, char **argv)
{           
    printf("[MYSHELL] $ ");

    while (TRUE) {
        user_input = getchar();
        switch (user_input) {

            case EOF:
                exit(-1);

            case '\n':
                printf("[MYSHELL] $ ");
                break;

            default:
                // parse input into cmd_argv - store # commands in cmd_argc
                handle_user_input();

                //determine input and execute foreground/background process
                execute_command();
        }
        background = 0;
    }
    printf("\n[MYSHELL] $ ");
    return 0;    
}

handle_user_input只需填充cmd_argv数组以执行,如果用户希望输出到文件,则user_input删除>并设置标志。output这是该方法的核心:

while (buffer_pointer != NULL) { 
        cmd_argv[cmd_argc] = buffer_pointer;
        buffer_pointer = strtok(NULL, " ");

        if(strcmp(cmd_argv[cmd_argc], ">") == 0){
            printf("\nThere was a '>' in %s @ index: %d for buffer_pointer: %s \n", *cmd_argv,cmd_argc,buffer_pointer);
            cmd_argv[cmd_argc] = strtok(NULL, " ");
            output = 1;
        }

        cmd_argc++;

        if(output){
            filename = buffer_pointer;
            printf("The return of handling input for filename %s =  %s + %s \n", buffer_pointer, cmd_argv[0], cmd_argv[1]); 
            return;
        }        
}

execute_command然后调用,解释现在填充的cmd_argv. 只是为了让您了解全局。显然,这些情况都不匹配,并且create_process调用了该方法:

int execute_command()
{
    if (strcmp("pwd", cmd_argv[0]) == 0){
        printf("%s\n",getenv("PATH"));  
        return 1;
    }
    else if(strcmp("cd", cmd_argv[0]) == 0){
        change_directory();
        return 1;        
    }
    else if (strcmp("jobs", cmd_argv[0]) == 0){
        display_job_list();
        return 1;   
    }
    else if (strcmp("kill", cmd_argv[0]) == 0){
        kill_job();
    }
    else if (strcmp("EOT", cmd_argv[0]) == 0){
        exit(1);
    }
    else if (strcmp("exit", cmd_argv[0]) == 0){
        exit(-1);
    }
    else{
        create_process();
        return;
    }
}

很直截了当,对吧?


create_process是我遇到问题的地方。

void create_process()
{
    status = 0;
    int pid = fork();
    background = 0;

    if (pid == 0) {
        // child process
        if(output){
            printf("Output set in create process to %d\n",output);
            output = 0;
            int output_fd = open(filename, O_RDONLY);
            printf("Output desc = %d\n",output_fd);
            if (output_fd > -1) {
                dup2(output_fd, STDOUT_FILENO);
                close(output_fd);
            } else {
                perror("open");
            }
        }
        printf("Executing command, but STDOUT writing to COMMAND PROMPT instead of FILE - as I get the 'open' error above \n");
        execvp(*cmd_argv,cmd_argv);
        // If an error occurs, print error and exit
        fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
        exit(0);
    } else {
        // parent process, waiting on child process
            waitpid(pid, &status, 0);
        if (status != 0)
            fprintf  (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
    }
    return;
}

我的印刷品output_fd = -1,我设法得到perror("open")里面的其他说明:open: No such file or directory。然后它打印出它是"writing to COMMAND PROMPT instead of FILE",正如我在控制台上显示的那样。然后执行execvpwhich handles cat foo.txt,但将其打印到控制台而不是文件。

我意识到此时不应该这样做,因为它output_fd = -1是不可取的,应该返回另一个值;但我不知道如何正确使用文件描述符来打开一个新的/现有的文件cat foo.txt > bar.txt并写入它,以及返回到命令行的标准输入。

我已经设法输出到文件,但后来失去了正确的标准输入。有人可以在这里指导我吗?我觉得我在做一些愚蠢的事情,因为我做错了或正在看。

非常感谢任何帮助。

4

2 回答 2

2

如果要写入文件,为什么要使用 O_RDONLY?我的猜测是你应该使用类似的东西:

int output_fd = open(filename, O_WRONLY|O_CREAT, 0666);

(0666是在创建时设置访问权限)。

显然,如果您无法打开已修改的文件,则不应启动该命令。

于 2011-02-25T19:16:37.110 回答
1

首先,我注意到很明显的是您已经打开了文件 O_RDONLY。输出不会那么好!

其次,重定向输出的基本过程是:

  • 打开文件进行写入
  • dup stdout,以便您可以在需要时保留一份副本。如果重定向,则与 stderr 相同。
  • fcntl 复制到 CLOEXEC(或者,使用 dup3)
  • dup2 文件到标准输出
  • 执行命令

最后,您是否真的将命令名称作为全局变量传递?cat foo | ( cat bar; echo hi; cat ) > baz我认为一旦您尝试实施或类似的事情,这将再次困扰您。

于 2011-02-25T19:21:21.017 回答