çMinishell添加管道 [英] C Minishell Adding Pipelines

查看:89
本文介绍了çMinishell添加管道的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So I'm making a UNIX minishell, and am trying to add pipelines, so I can do things like this:

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

不过我在遇到麻烦缠绕我的头周围的管路部分。我更换所有的|与0个字符,然后运行每一行作为法线。不过,我试图转移输出和输入。一个命令的输入需要是previous命令的输出,以及一个命令的输出必须是下一个命令的输入。

However I'm having trouble wrapping my head around the piping part. I replace all the "|" characters with 0, and then run each line as a normal line. However, I am trying to divert the output and input. The input of a command needs to be the output of the previous command, and the output of a command needs to be the input of the next command.

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

I'm doing this using pipes, however I can't figure out where to call pipe() and where to close them. From the main processing function, processline(), I have this code:

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

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

So, I'm recursively(the code above is in processline) sending the commands in between the "|" to be processed by processline. You can see where I commented out the code above, I'm not sure how to make it work. The 2nd and 3rd parameter of processline are the inputFD and outputFD respectively, so I need to process a command, write the output to a pipe, and then call processline again on the next command, however this time the output of the previous command is the input. This just doesn't seem like it can work though, because each time I close fd[0] I'm losing the previous output. Do I need two separate pipes, that I can flip flop back and forth with?

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

I'm just having trouble seeing how this is possible with a single pipe, if you guys need any additional info just ask. Here's the entire processline function in case you want to take a look:

http://pastebin.com/YiEdaYdj

编辑:如果有人已经实现管道我会爱一个链接到源外壳的例子,我一直没能找到一个对谷歌迄今

If anybody has an example of a shell that implements pipelines I would love a link to the source, I haven't been able to find one on google so far.

EDIT2:这里是我的predicament的例子:

Here's an example of my predicament:

echo a | echo b | echo c

首先,我会打电话的外壳是这样的:

So first I would call the shell like this:

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

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

Each of these occurs once per iteration, and as you can see I can't figure out what to pass for the input-file-descriptors and the output-file-descriptors for the 2nd and 3rd(and so on) iteration.

推荐答案

下面是一些中等普通而简单code执行管线,一个程序我打电话管道。它在一个单一的文件为presented的SSCCE,虽然我有文件 stderr.h stderr.c 在图书馆单独的文件与我所有的程序联系起来。 (其实,我有一个更复杂的在我的真实功能 stderr.c stderr.h ,但是这是一个很好的起点。)

Here's some moderately generic but simple code to execute pipelines, a program I'm calling pipeline. It's an SSCCE in a single file as presented, though I'd have the files stderr.h and stderr.c as separate files in a library to be linked with all my programs. (Actually, I have a more complex set of functions in my 'real' stderr.c and stderr.h, but this is a good starting point.)

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

The code operates in two ways. If you supply no arguments, then it runs a built-in pipeline:

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

这计数的时间每个人都记录在系统上的数字,presenting增加会话数的顺序列表。或者,您可以用那些你想调用的命令行参数的顺序调用,使用带引号管|(或| ),以单独的命令:

This counts the number of times each person is logged in on the system, presenting the list in order of increasing number of sessions. Alternatively, you can invoke with a sequence of arguments that are the command line you want invoked, use a quoted pipe '|' (or "|") to separate commands:

有效期:

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 '|'

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

The last three invocations enforce 'pipes as separators'. The code does not error check every system call; it does error check fork(), execvp() and pipe(), but skips checking on dup2() and close(). It doesn't include diagnostic printing for the commands that are generated; a -x option to pipeline would be a sensible addition, causing it to print out a trace of what it does. It also does not exit with the exit status of the last command in the pipeline.

注意,code的小孩被分支开始。这孩子将成为管道的最后一个过程,但首先创建一个管道,叉另一个进程运行在管道中较早的过程。相互递归函数不可能是整理出来的东西的唯一途径,但他们留下最少code重复(在code的早期草案有的内容exec_nth_command() exec_pipeline()和 exec_pipe_command())。

Note that the code starts with a child being forked. The child will become the last process in the pipeline, but first creates a pipe and forks another process to run the earlier processes in the pipeline. The mutually recursive functions are unlikely to be the only way of sorting things out, but they do leave minimal code repetition (earlier drafts of the code had the content of exec_nth_command() largely repeated in exec_pipeline() and exec_pipe_command()).

这里的方法,结构是这样的,原始进程只知道在管道中的最后一道工序。有可能重新设计以这样的方式,原始进程是在流水线的每进程的父的东西,所以原来的方法可以在每个指令的流水线中的状态分别报告。我还没修改code允许对结构;这将是一个稍微复杂一些,虽然不是那么可怕。

The process structure here is such that the original process only knows about the last process in the pipeline. It is possible to redesign things in such a way that the original process is the parent of every process in the pipeline, so the original process can report separately on the status of each command in the pipeline. I've not yet modified the code to allow for that structure; it will be a little more complex, though not hideously so.

/* 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:


Signals and SIGCHLD

The POSIX Signal Concepts section discusses SIGCHLD:

在SIG_DFL:

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

If the default action is to ignore the signal, delivery of the signal shall have no effect on the process.

在SIG_IGN:

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

If the action for the SIGCHLD signal is set to SIG_IGN, child processes of the calling processes shall not be transformed into zombie processes when they terminate. If the calling process subsequently waits for its children, and the process has no unwaited-for children that were transformed into zombie processes, it shall block until all of its children terminate, and wait(), waitid(), and waitpid() shall fail and set errno to [ECHILD].

<$c$c><signal.h>有对信号的默认意向表,并为SIGCHLD,默认为I(SIG_IGN)。

The description of <signal.h> has a table of default dispositions for signals, and for SIGCHLD, the default is I (SIG_IGN).

我增加了一个功能,在code以上:

I added another function to the code above:

#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)。我通过运行验证其操作:

I called it immediately after the call to err_setarg0(), and it reports 'Default' on both Mac OS X 10.7.5 and Linux (RHEL 5, x86/64). I validated its operation by running:

(trap '' CHLD; pipeline)

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

On both platforms, that reported 'Ignored', and the pipeline command no longer reported the exit status of the child; it didn't get it.

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

So, if the program is ignoring SIGCHLD, it does not generate any zombies, but does wait until 'all' of its children terminate. That is, until all of its direct children terminate; a process cannot wait on its grandchildren or more distant progeny, nor on its siblings, nor on its ancestors.

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

On the other hand, if the setting for SIGCHLD is the default, the signal is ignored, and zombies are created.

这对书面这一计划最方便的行为。在 corpse_collector()函数有一个循环,收集来自任何儿童的状态信息。只有一个在这个code一段时间的孩子;流水线的其余部分在管道中的最后处理的运行作为孩子(孩子,孩子的,...)。

That's the most convenient behaviour for this program as written. The corpse_collector() function has a loop that collects the status information from any children. There's only one child at a time with this code; the rest of the pipeline is run as a child (of the child, of the child, ...) of the last process in the pipeline.

不过我在与僵尸/尸体麻烦。我的老师让我实现它,你做的方式相同,为 CMD1 不是 CMD2 的的情况下父的: CMD1 | CMD2 | CMD3 。除非我告诉我的壳等上的每个进程( CMD1 CMD2 CMD3 ),而不仅仅是等待的最后一个进程( CMD3 ),整个管道关闭输出之前可以到达终点。我无法找出一个好办法,等待在他们身上。我的老师说使用WNOHANG。

However I'm having trouble with zombies/corpses. My teacher had me implement it the same way you did, as cmd1 isn't the parent of cmd2 in the case of: "cmd1 | cmd2 | cmd3". Unless I tell my shell to wait on each process (cmd1, cmd2, and cmd3), rather than just waiting on the last process (cmd3), the entire pipeline shuts down before the output can reach the end. I'm having trouble figuring out a good way to wait on them; my teacher said to use WNOHANG.

我不知道我理解的问题。随着code口提供, CMD3 CMD2 的母公司,而 CMD2 CMD1 在3命令管道父(和壳 CMD3 ),所以外壳也只能等待 CMD3 。我做了最初​​的状态:

I'm not sure I understand the problem. With the code I provided, cmd3 is the parent of cmd2, and cmd2 is the parent of cmd1 in a 3-command pipeline (and the shell is the parent of cmd3), so the shell can only wait on cmd3. I did state originally:

这里的方法,结构是这样的,原始进程只知道在管道中的最后一道工序。有可能重新设计以这样的方式,原始进程是在流水线的每进程的父的东西,所以原来的方法可以在每个指令的流水线中的状态分别报告。我还没修改code允许对结构;这将是一个稍微复杂一些,虽然不是那么可怕。

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

If you've got your shell able to wait on all three commands in the pipeline, you must be using the alternative organization.

waitpid函数()描述包括:

pid参数指定请求了状态一组子进程。该waitpid函数()函数只能从这个组返回一个子进程的状态:

The pid argument specifies a set of child processes for which status is requested. The waitpid() function shall only return the status of a child process from this set:


      
  • 如果PID等于(将为pid_t)-1,状态请求的子进程。在这方面,waitpid函数()然后相当于等待()

  • If pid is equal to (pid_t)-1, status is requested for any child process. In this respect, waitpid() is then equivalent to wait().

如果PID大于0,它指定为其请求状态的单个子进程的进程ID。

If pid is greater than 0, it specifies the process ID of a single child process for which status is requested.

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

If pid is 0, status is requested for any child process whose process group ID is equal to that of the calling process.

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

If pid is less than (pid_t)-1, status is requested for any child process whose process group ID is equal to the absolute value of pid.

选项参数从构建按位包或零个或多个下列标志,标题中定义的:

The options argument is constructed from the bitwise-inclusive OR of zero or more of the following flags, defined in the header:

...

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

WNOHANG The waitpid() function shall not suspend execution of the calling thread if status is not immediately available for one of the child processes specified by pid.

...

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

This means that if you're using process groups and the shell knows which process group the pipeline is running in (for example, because the pipeline is put into its own process group by the first process), then the parent can wait for the appropriate children to terminate.

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

...rambling... I think there's some useful information here; there probably should be more that I'm writing, but my mind's gone blank.

这篇关于çMinishell添加管道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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