çMinishell添加管道(C Minishell Adding Pipelines)

2019-06-21 09:14发布

所以我在做一个UNIX minishell,并正尝试添加管道,所以我可以做这样的事情:

ps aux | grep dh | grep -v grep | cut -c1-5

但是我无法缠绕我的头周围的管路部分。 我代替所有的“|” 与0个字符,然后运行每一行作为法线。 不过,我试图转移输出和输入。 一个命令的输入必须是先前的命令的输出,以及命令的输出必须是下一个命令的输入。

我这样做使用管道,但我想不出哪里调用管道(),并在那里将其关闭。 从主处理功能,ProcessLine从(),我有这样的代码:

if((pix = findUnquotChar(line_itr, '|')))
{
    line_itr[pix++] = 0;
    if(pipe (fd) < 0) perror("pipe");
    processline(line_itr, inFD, fd[1], pl_flags);
    line_itr = &(line_itr[pix]);

    while((pix = findUnquotChar(line_itr, '|')) && pix < line_len)
    {
        line_itr[pix++] = 0;
        //? if(pipe (fd) < 0) perror("pipe");
        processline(line_itr, fd[0], fd[1] pl_flags);
        line_itr = &(line_itr[pix]);
    //? close(fd[0]);
    //? close(fd[1]);
    }
    return;
}

所以,我递归(上面的代码是ProcessLine从)之间的发送命令“|” 由ProcessLine从被处理。 你可以看到我注释掉的代码上面,我不知道如何使它发挥作用。 ProcessLine从的第二和第三个参数是inputFD和outputFD分别,所以我需要处理一个命令,将输出写入到管道,然后在接下来的命令再次调用ProcessLine从,但此时前一个命令的输出是输入。 这似乎并不像它可以工作,虽然,因为每次我关闭FD时间[0]我失去了以前的输出。 我需要两个独立的管道,我可以触发器来回?

我只是无法看到这怎么可能用一个单管,如果你们需要任何额外的信息只问。 下面是如果你想看看整个ProcessLine从功能:

http://pastebin.com/YiEdaYdj

编辑:如果有人有实现管线我喜欢的链接源的壳的一个例子,我一直没能找到一个对谷歌至今。

EDIT2:这里是我的困境的一个例子:

echo a | echo b | echo c

因此,首先我会叫这样的外壳:

processline("echo a", 0, fd[1], flags);

....

processline("echo b", fd[0], NOT_SURE_GOES_HERE[1], flags);

....

processline("echo c", NOT_SURE_GOES_HERE[0], NOT_SURE_EITHER[1], flags);

每一种发生每次迭代一次,你可以看到我想不出什么以通为输入文件描述符和输出文件描述符的第二和第三(依此类推)迭代。

Answer 1:

下面是一些中等普通而简单的代码来执行管线,一个程序我打电话pipeline 。 这是在一个单一的文件中SSCCE作为提出,虽然我有文件stderr.hstderr.c在图书馆单独的文件,用我所有的计划联系起来。 (其实,我有一个更复杂的在我的“真实”功能stderr.cstderr.h ,但这是一个很好的起点。)

该代码以两种方式进行操作。 如果提供任何参数,那么它运行一个内置的管道:

who | awk '{print $1}' | sort | uniq -c | sort -n

这种计算的时间每个人都记录在系统上的数字,在增加会话数的顺序呈现列表。 另外,您也可以与那些有你想调用的命令行参数的顺序调用,使用带引号的管道'|' (或"|" )来分隔命令:

有效:

pipeline
pipeline ls '|' wc
pipeline who '|' awk '{print $1}' '|' sort '|' uniq -c '|' sort -n
pipeline ls

无效:

pipeline '|' wc -l
pipeline ls '|' '|' wc -l
pipeline ls '|' wc -l '|'

最后三个调用执行“管道作为分隔符”。 代码不会错误检查每个系统调用; 它错误检查fork() execvp()pipe()但不会检查dup2()close() 。 它不包括所生成的命令诊断印刷; 一个-x选项pipeline将是一个明智另外,使其打印出来是做什么的痕迹。 它也不会与最后一个命令在管道的退出状态退出。

请注意,该代码与儿童被分支开始。 这孩子将成为管道的最后一个过程,但首先创建一个管道,叉另一个进程运行在管道中的早期过程。 相互递归函数是不太可能整理出来的东西的唯一途径,但他们留下最少的代码重复(代码前几稿中有内容exec_nth_command()在很大程度上重复exec_pipeline()exec_pipe_command()

这里的工艺结构是这样的,原来过程只知道在管道中的最后一道工序。 有可能重新设计以这样的方式,原来的工艺是在每一个管道进程的父的事情,所以原来的过程可以在每个命令在管道的状态分别报告。 我还没修改了代码,以便对结构; 这将是一个稍微复杂一些,虽然不是那么可怕。

/* One way to create a pipeline of N processes */

/* stderr.h */
#ifndef STDERR_H_INCLUDED
#define STDERR_H_INCLUDED

static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_syswarn(char const *fmt, ...);

#endif /* STDERR_H_INCLUDED */

/* pipeline.c */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
/*#include "stderr.h"*/

typedef int Pipe[2];

/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);

/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds)
{
    assert(ncmds >= 1);
    if (ncmds > 1)
    {
        pid_t pid;
        Pipe input;
        if (pipe(input) != 0)
            err_sysexit("Failed to create pipe");
        if ((pid = fork()) < 0)
            err_sysexit("Failed to fork");
        if (pid == 0)
        {
            /* Child */
            exec_pipe_command(ncmds-1, cmds, input);
        }
        /* Fix standard input to read end of pipe */
        dup2(input[0], 0);
        close(input[0]);
        close(input[1]);
    }
    execvp(cmds[ncmds-1][0], cmds[ncmds-1]);
    err_sysexit("Failed to exec %s", cmds[ncmds-1][0]);
    /*NOTREACHED*/
}

/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output)
{
    assert(ncmds >= 1);
    /* Fix stdout to write end of pipe */
    dup2(output[1], 1);
    close(output[0]);
    close(output[1]);
    exec_nth_command(ncmds, cmds);
}

/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds)
{
    assert(ncmds >= 1);
    pid_t pid;
    if ((pid = fork()) < 0)
        err_syswarn("Failed to fork");
    if (pid != 0)
        return;
    exec_nth_command(ncmds, cmds);
}

/* Collect dead children until there are none left */
static void corpse_collector(void)
{
    pid_t parent = getpid();
    pid_t corpse;
    int   status;
    while ((corpse = waitpid(0, &status, 0)) != -1)
    {
        fprintf(stderr, "%d: child %d status 0x%.4X\n",
                (int)parent, (int)corpse, status);
    }
}

/*  who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = { "who",                0 };
static char *cmd1[] = { "awk",  "{print $1}", 0 };
static char *cmd2[] = { "sort",               0 };
static char *cmd3[] = { "uniq", "-c",         0 };
static char *cmd4[] = { "sort", "-n",         0 };

static char **cmds[] = { cmd0, cmd1, cmd2, cmd3, cmd4 };
static int   ncmds = sizeof(cmds) / sizeof(cmds[0]);

static void exec_arguments(int argc, char **argv)
{
    /* Split the command line into sequences of arguments */
    /* Break at pipe symbols as arguments on their own */
    char **cmdv[argc/2];            // Way too many
    char  *args[argc+1];
    int cmdn = 0;
    int argn = 0;

    cmdv[cmdn++] = &args[argn];
    for (int i = 1; i < argc; i++)
    {
        char *arg = argv[i];
        if (strcmp(arg, "|") == 0)
        {
            if (i == 1)
                err_sysexit("Syntax error: pipe before any command");
            if (args[argn-1] == 0)
                err_sysexit("Syntax error: two pipes with no command between");
            arg = 0;
        }
        args[argn++] = arg;
        if (arg == 0)
            cmdv[cmdn++] = &args[argn];
    }
    if (args[argn-1] == 0)
        err_sysexit("Syntax error: pipe with no command following");
    args[argn] = 0;
    exec_pipeline(cmdn, cmdv);
}

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    if (argc == 1)
    {
        /* Run the built in pipe-line */
        exec_pipeline(ncmds, cmds); 
    }
    else
    {
        /* Run command line specified by user */
        exec_arguments(argc, argv);
    }
    corpse_collector();
    return(0);
}

/* stderr.c */
/*#include "stderr.h"*/
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

static const char *arg0 = "<undefined>";

static void err_setarg0(const char *argv0)
{
    arg0 = argv0;
}

static void err_vsyswarn(char const *fmt, va_list args)
{
    int errnum = errno;
    fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
    vfprintf(stderr, fmt, args);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    putc('\n', stderr);
}

static void err_syswarn(char const *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    err_vsyswarn(fmt, args);
    va_end(args);
}

static void err_sysexit(char const *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    err_vsyswarn(fmt, args);
    va_end(args);
    exit(1);
}

信号和SIGCHLD

POSIX的信号的概念部分讨论SIGCHLD:

SIG_DFL在:

如果默认动作是忽略这个信号,信号的传递有权对过程没有影响。

在SIG_IGN:

如果为SIGCHLD信号的操作设置为SIG_IGN,则调用进程的子进程不得时,他们终止转化为僵尸进程。 如果调用进程随后等待它的孩子,过程中没有unwaited,对于转化成僵尸程序中的儿童,应当阻塞,直到所有的子终止,并等待() , waitid()和waitpid函数()应失败并设置errno为[ECHILD]

的描述<signal.h>具有默认为倾向信号的表,以及用于SIGCHLD,缺省值是I(SIG_IGN)。


我添加另一个函数上面的代码:

#include <signal.h>

typedef void (*SigHandler)(int signum);

static void sigchld_status(void)
{
    const char *handling = "Handler";
    SigHandler sigchld = signal(SIGCHLD, SIG_IGN);
    signal(SIGCHLD, sigchld);
    if (sigchld == SIG_IGN)
        handling = "Ignored";
    else if (sigchld == SIG_DFL)
        handling = "Default";
    printf("SIGCHLD set to %s\n", handling);
}

我的呼叫后,立即把它称为err_setarg0()并报告'默认'在Mac OS X 10.7.5和Linux(RHEL 5,86/64)。 我通过运行验证其操作:

(trap '' CHLD; pipeline)

在这两个平台上,报道的“忽略”和pipeline命令不再报告孩子的退出状态; 它没有得到它。

因此,如果该程序被忽略SIGCHLD,它不产生任何僵尸,但等到其孩子终止“所有”。 也就是说,直到其所有直接子女的终止; 一个进程不能等待其孙子或更遥远的后代,也不在它的兄弟姐妹,也没有在它的祖先。

在另一方面,如果SIGCHLD设置为默认值,则信号被忽略,并创建僵尸。

这对书面这一计划的最方便的行为。 该corpse_collector()函数有一个循环,收集来自任何孩子的状态信息。 这里只有一个在使用此代码时孩子; 管道的其余部分运行在管道中的最后一道工序的孩子(孩子的,孩子的,......)。


但是我在与僵尸/尸体麻烦。 我的老师让我实现它你没有以同样的方式,因为cmd1不是父cmd2中的情况:“ cmd1 | cmd2 | cmd3 ”。 除非我告诉我的壳等上的每个进程( cmd1cmd2cmd3 ),而不仅仅是等待的最后一个进程( cmd3 ),整个管线关闭输出之前可以到达终点。 我无法找出一个好办法,等待在他们身上。 我的老师说,使用WNOHANG。

我不知道我理解这个问题。 与我提供的代码, cmd3是父cmd2 ,和cmd2是的父cmd1在3命令管道(和壳是的父cmd3 ),所以壳只能等待cmd3 。 我做了最初的状态:

这里的工艺结构是这样的,原来过程只知道在管道中的最后一道工序。 有可能重新设计以这样的方式,原来的工艺是在每一个管道进程的父的事情,所以原来的过程可以在每个命令在管道的状态分别报告。 我还没修改了代码,以便对结构; 这将是一个稍微复杂一些,虽然不是那么可怕。

如果你有你的外壳能够等待管道中的所有三个命令,你必须使用可替代组织。

waitpid()描述包括:

pid参数指定请求了状态一组子进程。 该waitpid函数()函数将只返回此集的子进程的状态:

  • 如果pid等于(将为pid_t)-1,状态请求任意子进程。 在这方面,waitpid函数()是等效然后等待()。

  • 如果pid大于0,它规定被请求状态的单个子进程的进程ID。

  • 如果pid为0,状态要求所有子进程,其进程组ID等于调用进程的。

  • 如果pid为小于(将为pid_t)-1,状态请求任意子进程,其进程组ID等于PID绝对值。

选项参数是从构成按位包容OR的以下标志,在报头中定义的零个或多个:

...

WNOHANG的waitpid()如果状态没有立即由pid指定的子进程的一个功能不得暂停调用线程的执行。

...

这意味着,如果你使用的过程组和外壳知道管道中运行的进程组(例如,因为管道被放入其自身的进程组第一进程),那么家长可以等待适当的孩子终止。

... ...散漫 ,我认为这里有一些有用的信息; 有可能应该更多的是我写的,但我的头脑一片空白。



文章来源: C Minishell Adding Pipelines