使用C在Linux Shell中实现输入/输出重定向 [英] Implementing input/output redirection in a Linux shell using C

查看:119
本文介绍了使用C在Linux Shell中实现输入/输出重定向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C为Linux创建一个基本的shell.在我尝试进行输出重定向之前,它已经起作用,并且它破坏了一切.当我运行此代码时,它直接进入fork()的默认情况.我不知道为什么.如果我摆脱了子进程中的for循环,它就可以工作,但是即使有了for循环,我也不明白为什么子进程甚至都不会输入.如果我将打印语句放在子进程的顶部,则不会打印出来.

I am trying to create a basic shell for Linux using C. I have gotten it to work until I try to do output redirection and it just destroys everything. When I run this code, it goes straight to the default case of the fork(). I have no idea why. If I get rid of the for loop in the child process it works, but even with the for loop I don't understand why the child process is never even entered. If I put a print statement at the top of the child process it doesn't get printed.

当我在命令行中运行此命令时,我得到提示并键入类似"ls"的命令,该命令在添加for循环之前有效,但是现在我仅收到%am i here",如果按Enter即可一直给我同样的话.我的目标是能够键入"ls>输出"并使其正常工作.我认为输入重定向有效,但是说实话,我什至没有花太多时间,因为我对输出重定向发生的事情非常困惑.任何帮助将不胜感激,我花了4个小时在相同的时间(如15条线)上尝试使之起作用.

When I run this in command line, I get the prompt and type something like "ls", which worked before I added the for loop, but now I just get "% am i here" and if I press enter it just keeps giving me that same line. My goal is to be able to type "ls > output" and have it work. I think the input redirection works, but honestly I haven't even played with it much because I am so utterly confused as to what is going on with the output redirection. Any help would be greatly appreciated, I've spent 4 hours on the same like 15 lines trying to get this to work.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#define HISTORY_COUNT 20
char *prompt = "% ";

int
main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char **args3;
char cmd[100];
char *hp;
char *cp;
char *ifile;
char *ofile;
int check;
int pfds[2];
int i;
int j;
int current = 0;
int p = 0;
//int check;
char *hist[HISTORY_COUNT];
//char history[90];
//typedef void (*sighandler_t) (int);

args = malloc(80 * sizeof(char *));
args2 = malloc(80 * sizeof(char *));

signal(SIGINT, SIG_IGN);

while (1) {
    fprintf(stderr, "%s", prompt);
    fflush(stderr);

    if (fgets(line, 80, stdin) == NULL)
        break;


    // split up the line

    i = 0;
    while (1) {
        token = strtok((i == 0) ? line : NULL, separator);
        if (token == NULL)
            break;
        args[i++] = token;

             /* build command array */

    }
    args[i] = NULL;

    if (i == 0){
        continue;
    }

    // assume no redirections
    ofile = NULL;
    ifile = NULL;

    // split off the redirections
    j = 0;
    i = 0;
    while (1) {        //stackoverflow.com/questions/35569673
        cp = args[i++];
        if (cp == NULL)
            break;

        switch (*cp) {
        case '<':
            if (cp[1] == 0)
                cp = args[i++];
            else
                ++cp;
            ifile = cp;
            break;

        case '>':
            if (cp[1] == 0)
                cp = args[i++];
            else
                ++cp;
            ofile = cp;
            break;
    case '|':

        if(cp[1] ==0){
           cp = args[i++];
           if(pipe(pfds) == -1){
               perror("Broken Pipe");
               exit(1);
           }
       p = 1;
    }
    else{ 
       ++cp;

    }
       break;

        default:
            args2[j++] = cp;
        args3[cp++] = cp
            break;
        }
    }
    args2[j] = NULL;
    if (j == 0)
        continue;

    switch (pid = fork()) {
        case 0:
            // open stdin
            if (ifile != NULL) {
                int fd = open(ifile, O_RDONLY);

                if (dup2(fd, STDIN_FILENO) == -1) {
                    fprintf(stderr, "dup2 failed");
                }

                close(fd);
            }


            // open stdout
            if (ofile != NULL) {
                // args[1] = NULL;
                int fd2;


                if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) {
                    perror("couldn't open output file.");
                    exit(0);
                }

                // args+=2;
                printf("okay");
                dup2(fd2, STDOUT_FILENO);
                close(fd2);
            }


        if(p == 1){        //from stackoverflow.com/questions/2784500
        close(1);
        dup(pfds[1]);
        close(pfds[0]);
        execvp(args2[0], args2);
        break;
        }


        if(strcmp(args2[0], "cd") == 0){            //cd command
            if(args2[1] == NULL){
                fprintf(stderr, "Expected argument");
            }
            else{

            check = chdir(args2[1]);

        if(check != 0){
            fprintf(stderr,"%s",prompt);

            }
           }
        break;
        }

        execvp(args2[0], args2);        /* child */
        signal(SIGINT, SIG_DFL);
        fprintf(stderr, "ERROR %s no such program\n", line);
        exit(1);
        break;

    case -1:
        /* unlikely but possible if hit a limit */
        fprintf(stderr, "ERROR can't create child process!\n");
        break;

    default:
        //printf("am I here");
    if(p==1){
        close(0);
        dup(pfds[0]);
        close(pfds[1]);
        //execvp();
    }
        wait(NULL);
        //waitpid(pid, 0, 0);
    }
}

exit(0);

}

推荐答案

我添加了一个单独的参数传递来捕获和记住I/O重定向,并将其从传递给子级的arg列表中删除.

I added a separate argument pass to capture and remember the I/O redirections and remove them from the arg list passed to the child.

这是更正的代码[请原谅免费的样式清理]:

Here's the corrected code [please pardon the gratuitous style cleanup]:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>

char *prompt = "% ";

int
main()
{
    int pid;
    //int child_pid;
    char line[81];
    char *token;
    char *separator = " \t\n";
    char **args;
    char **args2;
    char *cp;
    char *ifile;
    char *ofile;
    int i;
    int j;
    int err;
    //int check;
    //char history[90];
    //typedef void (*sighandler_t) (int);

    args = malloc(80 * sizeof(char *));
    args2 = malloc(80 * sizeof(char *));

    //signal(SIGINT, SIG_IGN);

    while (1) {
        fprintf(stderr, "%s", prompt);
        fflush(stderr);

        if (fgets(line, 80, stdin) == NULL)
            break;

        // split up the line
        i = 0;
        while (1) {
            token = strtok((i == 0) ? line : NULL, separator);
            if (token == NULL)
                break;
            args[i++] = token;              /* build command array */
        }
        args[i] = NULL;
        if (i == 0)
            continue;

        // assume no redirections
        ofile = NULL;
        ifile = NULL;

        // split off the redirections
        j = 0;
        i = 0;
        err = 0;
        while (1) {
            cp = args[i++];
            if (cp == NULL)
                break;

            switch (*cp) {
            case '<':
                if (cp[1] == 0)
                    cp = args[i++];
                else
                    ++cp;
                ifile = cp;
                if (cp == NULL)
                    err = 1;
                else
                    if (cp[0] == 0)
                        err = 1;
                break;

            case '>':
                if (cp[1] == 0)
                    cp = args[i++];
                else
                    ++cp;
                ofile = cp;
                if (cp == NULL)
                    err = 1;
                else
                    if (cp[0] == 0)
                        err = 1;
                break;

            default:
                args2[j++] = cp;
                break;
            }
        }
        args2[j] = NULL;

        // we got something like "cat <"
        if (err)
            continue;

        // no child arguments
        if (j == 0)
            continue;

        switch (pid = fork()) {
        case 0:
            // open stdin
            if (ifile != NULL) {
                int fd = open(ifile, O_RDONLY);

                if (dup2(fd, STDIN_FILENO) == -1) {
                    fprintf(stderr, "dup2 failed");
                }

                close(fd);
            }

            // trying to get this to work
            // NOTE: now it works :-)
            // open stdout
            if (ofile != NULL) {
                // args[1] = NULL;
                int fd2;

                //printf("PLEASE WORK");
                if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) {
                    perror("couldn't open output file.");
                    exit(0);
                }

                // args+=2;
                printf("okay");
                dup2(fd2, STDOUT_FILENO);
                close(fd2);
            }

            execvp(args2[0], args2);        /* child */
            signal(SIGINT, SIG_DFL);
            fprintf(stderr, "ERROR %s no such program\n", line);
            exit(1);
            break;

        case -1:
            /* unlikely but possible if hit a limit */
            fprintf(stderr, "ERROR can't create child process!\n");
            break;

        default:
            //printf("am I here");
            wait(NULL);
            //waitpid(pid, 0, 0);
        }
    }

    exit(0);
}


更新:

如果您还在身边,您认为可以帮助我创建管道吗?

If you're still around do you think you could help me with creating a pipe?

好的.太大,无法在此处发布.请参阅: http://pastebin.com/Ny1w6pUh

Sure. It's too big to post here. See: http://pastebin.com/Ny1w6pUh

哇,您创建了所有3300条线吗?

Wow did you create all 3300 lines?

是的

我从我的另一个SO答案中借了xstr [带有错误修复和增强功能]. dlk是新的,但是我做了很多,所以很容易.大部分是新代码.

I borrowed xstr from another SO answer of mine [with bugfix and enhancement]. The dlk was new, but I do many of those, so was easy. Most of it was new code.

但是...它由我之前做过的片段/概念组成:tgb,FWD,BTV,sysmagic.注意,struct foo的所有结构成员都以foo_为前缀[我的标准].我在需要时使用带有DLHDEFDLKDEF的宏"trickery"来模拟继承/模板.

But ... It was composed of fragments/concepts I've done before: tgb, FWD, BTV, sysmagic. Notice all struct members for struct foo are prefixed with foo_ [standard for me]. The macro "trickery" with DLHDEF and DLKDEF to simulate inheritance/templates is also something I do [when necessary].

许多函数vars重用我的样式:idx用作索引var [我从不使用i/j,而是xidx/yidx],cp用作char指针,cnt表示计数,len表示字节长度等.因此,我不必思考"一些小东西[战术],而可以专注于战略.

Many function vars reuse my style: idx for index var [I would never use i/j, but rather xidx/yidx], cp for char pointer, cnt for count, len for byte length, etc. Thus, I don't have to "think" about small stuff [tactics] and can focus on strategy.

以上idx等. al.对我来说是一种签名风格".它不一定比其他人更好(或更糟).这是因为当链接器/加载器只能处理8个字符符号时,我开始使用C,因此简洁是关键.但是,我习惯于使用较短的名称.考虑哪个更清晰/更好:

The above idx et. al. is a "signature style" for me. It's not necessarily better [or worse] than others. It comes from the fact that I started using C when the linker/loader could only handle 8 character symbols, so brevity was key. But, I got used to using the shorter names. Consider which is clearer/better:

for (fooidx = 0;  fooidx <= 10;  ++fooidx)

或者:

for (indexForFooArray = 0;  indexForFooArray <= 10;  ++indexForFooArray)

我使用do { ... } while (0)来避免if/else梯形 lot .这称为一次通过"循环.这被认为是有争议的",但是以我的经验来看,它使代码更整洁.就我个人而言,我从来没有发现do/while循环的[更标准]用法,使用whilefor循环-YMMV不能更容易/更好.实际上,许多语言甚至根本没有do/while.

I use the do { ... } while (0) to avoid if/else ladders a lot. This is called a "once through" loop. This is considered "controversial", but, in my experience it keeps the code cleaner. Personally, I've never found a [more standard] use of a do/while loop that can't be done more easily/better with a while or for loop--YMMV. In fact, a number of languages don't even have do/while at all.

此外,我使用小写字母,除非它始终是大写的#define [或enum].也就是说,我使用蛇皮套"(例如fooidx)和不是驼峰皮套"(例如indexForFooArray).

Also, I use lower case unless it's a #define [or enum] which is always upper. That is, I use "snake case" (e.g. fooidx) and not "camel hump case" (e.g. indexForFooArray).

包含函数原型的.proto文件是自动生成的.这是节省时间的时间. 旁注:确保外部链接中至少有v2,因为Makefile中存在错误. make clean将删除.proto. v2不会这么做

The .proto file containing function prototypes is auto-generated. This is a huge time saver. Side note: Be sure you have at least v2 from the external link as there was a bug in the Makefile. A make clean would erase the .proto. v2 won't do that

这些年来,我发展了自己的风格.原来,Linux内核样式是从我的书中借来的".实际上不是:-)我的是第一位的.但是……与此同时,他们想出了一个与我的匹配度为99%的东西:/usr/src/kernels/whatever_version/Documentation/CodingStyle.

I've developed my own style over the years. Turns out that the linux kernel style was "borrowed from mine". Not actually :-) Mine came first. But ... They, in parallel, came up with something that is a 99% match to mine: /usr/src/kernels/whatever_version/Documentation/CodingStyle.

与给定样式(自己的样式)的一致性是关键.对于给定的函数,您不必担心要为变量命名,要使用的缩进或空白行.

Consistency to a given style [one's own] is key. For a given function, you don't have to worry about what you'll name the variables, what indent or blank lines you'll use.

这有助于读者/新开发者.他们可以阅读一些功能,查看演奏中的样式,然后由于所有功能具有相似的样式,因此运行速度更快.

This helps a reader/new developer. They can read a few functions, see the style in play, and then go faster because all functions have similar style.

所有这些使您可以更快",并且在第一次尝试时仍可以获得高质量的代码.我也相当经验丰富.

All this allows you to "go faster" and still get high quality code on the first try. I'm also quite experienced.

此外,我的代码注释集中在意图"上.就是说,您希望代码按照实际情况执行什么操作.他们应该回答什么/为什么",而代码则是如何".

Also, my code comments focus on "intent". That is, what do you want the code to do in real world terms. They should answer the "what/why" and the code is the "how".

这篇关于使用C在Linux Shell中实现输入/输出重定向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆