简体   繁体   English

Unix Shell在C中实现Cat-文件描述符问题

[英]Unix Shell Implementing Cat in C - File Descriptor Issue

I've about got my practice implementation of a Unix shell done, except I'm having an issue with implementing cat when its output is to a file; 我已经完成了Unix shell的实践实现,除了在cat输出到文件时实现cat 存在问题 IE: cat foo.txt > bar.txt - outputting foo 's contents to bar . IE: cat foo.txt > bar.txt foo的内容输出到bar

Let's start from the main function & then I'll define the submethods: 让我们从主要功能开始,然后定义子方法:

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 just populates the cmd_argv array to execute the user_input , and removes the > and sets an output flag if the user wishes to output to a file. handle_user_input只是填充cmd_argv数组以执行user_input ,如果用户希望输出到文件,则删除>并设置output标志。 This is the meat of that method: 这就是这种方法的本质:

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 is then called, interpreting the now populated cmd_argv . 然后调用execute_command ,解释现在填充的cmd_argv Just to give you an idea of the big picture. 只是为了让您了解大局。 Obviously, none of these cases match and the create_process method is called: 显然,这些情况都不匹配,并且调用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;
    }
}

Pretty straight forward, right? 很简单吧?


create_process is where I'm having issues. 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;
}

My printed output_fd = -1 , and I manage to get the perror("open") inside the else stating: open: No such file or directory . 我打印的output_fd = -1 ,我设法在else perror("open")得到perror("open")open: No such file or directory It then prints that it's "writing to COMMAND PROMPT instead of FILE" , as I display to the console. 然后,当我显示到控制台时,它会打印出它是"writing to COMMAND PROMPT instead of FILE" Then executes execvp which handles cat foo.txt , but prints it to the console instead of the file. 然后执行处理cat foo.txt execvp ,但将其打印到控制台而不是文件。

I realize it shouldn't at this point, as having output_fd = -1 isnt desirable and should be returning another value; 我意识到此时不应该这样做,因为不希望output_fd = -1并且应该返回另一个值; but I cant figure out how to use file descriptors correctly in order to open a new/existing file with cat foo.txt > bar.txt and write to it, as WELL AS GET BACK to the command line's stdin. 但是我无法弄清楚如何正确使用文件描述符,以便通过cat foo.txt > bar.txt打开一个新文件/现有文件并写入文件,就像回到命令行的stdin一样。

I have managed to output to the file, but then lose getting back the correct stdin. 我已经设法输出到文件,但是随后又丢失了正确的标准输入。 Could someone please direct me here? 有人可以把我引导到这里吗? I feel like I'm going in circles over something silly I'm doing wrong or looking over. 我觉得我正在做一些愚蠢的事情,我做错了或正在寻找。

Any help is greatly GREATLY appreciated. 非常感谢任何帮助。

Why do you use O_RDONLY if you want to write to the file? 如果要写入文件,为什么要使用O_RDONLY? My guess is that you should use something like: 我的猜测是您应该使用类似以下内容的方法:

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

(The 0666 is to set up the access rights when creating). (0666用于在创建时设置访问权限)。

And obviously, if you can't open a redicted file, you shouldn't launch the command. 显然,如果您无法打开某个目标文件,则不应启动该命令。

First, obvious thing I notice is that you've opened the file O_RDONLY. 首先,我注意到的很明显的事情是您已经打开了文件O_RDONLY。 Not going to work so well for output! 输出效果不好!

Second, basic process for redirecting the output is: 其次,重定向输出的基本过程是:

  • open file for writing 打开文件进行写入
  • dup stdout so you can keep a copy if needed. dup stdout,因此您可以根据需要保留副本。 same with stderr if redirecting. 如果重定向,则与stderr相同。
  • fcntl your duplicate to CLOEXEC (alternatively, use dup3) fcntl您的副本到CLOEXEC(或者,使用dup3)
  • dup2 file to stdout dup2文件到stdout
  • exec the command 执行命令

and finally, are you really passing around command names as global variables? 最后,您真的将命令名作为全局变量传递了吗? I think this will come back to haunt you once you try and implement cat foo | ( cat bar; echo hi; cat ) > baz 我认为,一旦您尝试实施cat foo | ( cat bar; echo hi; cat ) > baz就会再次困扰您cat foo | ( cat bar; echo hi; cat ) > baz cat foo | ( cat bar; echo hi; cat ) > baz or somesuch. cat foo | ( cat bar; echo hi; cat ) > baz或诸如此类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM