【问题标题】:Almost done linux shell pipe几乎完成了 linux shell 管道
【发布时间】:2013-12-06 22:58:37
【问题描述】:

嗨,我正在尝试在 linux 上构建一个 shell,但我被流水线部分困住了。首先我从用户那里获取输入,比如“ls | sort”,然后当我尝试运行程序时,它看起来像命令 ls 和 sort 不起作用 看起来我已经做对了所有事情,但它似乎仍然无法正常工作。你能帮忙吗?提前致谢

include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/stat.h>
#define CREATE_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int setup();

int main(int argc, const char * argv[])
{

    while(1)

    {
        printf("333sh: ");
        if(setup())
            break;
    }

    return 0;
}
int setup(){




    char  input [128];
    char *arg[32];
    int i = 1;
    while(fgets(input,128,stdin)!=NULL)
    {
        arg[0] = strtok(input," \n");
        while((arg[i]=strtok(NULL," \n")) != NULL){
            i++;
            }



           if (arg[1]!=NULL && strcmp(arg[1],"|")==0 && arg[2]!=NULL ){
            pid_t pid;

            int fd[3];

        pipe(fd);

    pid=fork();
        if(pid<0){
            printf("fork");
        }
        else if(pid==0){
            pid_t cpid;

            cpid=fork();
                if(cpid==0){

                    dup2(fd[2], 1); // Replace stdin with the read end of the pipe
                    close(fd[0]); // Don't need another copy of the pipe read end hanging about
                    close(fd[2]);
                    execvp(arg[0],arg);
        }
                else if(pid>0){

                    dup2(fd[0], 0); // Replace stdout with the write end of the pipe
                    close(fd[0]); //close read from pipe, in parent
                    close(fd[2]); // Don't need another copy of the pipe write end hanging about
                    execvp(arg[2], arg);
        }
    }
        else if(pid>0){
            waitpid(pid, NULL,0);
  }

        }


            }





    }

【问题讨论】:

    标签: c linux shell pipe dup2


    【解决方案1】:

    您最大的问题是您的命令的参数列表格式错误(在您解决了由Ben Jackson 在他的answer 中诊断的管道文件描述符的索引 2 与索引 1 问题之后)。

    我添加了一个函数:

    static void dump_args(int pid, char **argv)
    {
        int i = 0;
        fprintf(stderr, "args for %d:\n", pid);
        while (*argv != 0)
            fprintf(stderr, "%d: [%s]\n", i++, *argv++);
    }
    

    并在调用 execvp() 之前调用它,我得到的输出是:

    $ ./ns
    333sh: ls | sort
    args for 29780:
    0: [ls]
    1: [|]
    2: [sort]
    ls: sort: No such file or directory
    ls: |: No such file or directory
    ^C
    $
    

    control-C 是我打断程序。每个命令的参数必须是“命令名称”(通常是可执行文件的名称),后跟其余参数和一个空指针。

    您的标记化代码没有提供两个正确的命令。

    您还对正在查看的 PID 有疑问:

                    cpid = fork();
                    if (cpid == 0)
                    {
                        dup2(fd[1], 1);
                        close(fd[0]);
                        close(fd[1]);
                        dump_args(getpid(), arg);
                        execvp(arg[0], arg);
                        fprintf(stderr, "Failed to exec %s\n", arg[0]);
                        exit(1);
                    }
                    else if (pid > 0)  // should be cpid!
                    {
                        dup2(fd[0], 0);
                        close(fd[0]);
                        close(fd[1]);
                        dump_args(pid, arg);
                        execvp(arg[1], arg);
                        fprintf(stderr, "Failed to exec %s\n", arg[1]);
                        exit(1);
                    }
    

    你还需要在等待之前关闭父进程中的管道文件描述符。

    此代码针对简单的x | y 命令序列(例如ls | sortls | sort -r)进行编译和“工作”。然而,它远不是一个通用的解决方案。在找到通用解决方案之前,您需要大量修复参数解析代码。

    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    int setup(void);
    
    int main(void)
    {
        while (1)
        {
            printf("333sh: ");
            if (setup())
                break;
        }
        return 0;
    }
    
    static void dump_args(int pid, char **argv)
    {
        int i = 0;
        fprintf(stderr, "args for %d:\n", pid);
        while (*argv != 0)
            fprintf(stderr, "%d: [%s]\n", i++, *argv++);
    }
    
    int setup(void)
    {
        char input[128];
        char *arg[32];
        int i = 1;
        while (fgets(input, sizeof(input), stdin) != NULL)
        {
            arg[0] = strtok(input, " \n");
            while ((arg[i] = strtok(NULL, " \n")) != NULL)
            {
                i++;
            }
            if (arg[1] != NULL && strcmp(arg[1], "|") == 0 && arg[2] != NULL)
            {
                pid_t pid;
                int fd[2];
                arg[1] = NULL;
    
                pipe(fd);
    
                pid = fork();
                if (pid < 0)
                {
                    fprintf(stderr, "fork failed\n");
                    return 1;
                }
                else if (pid == 0)
                {
                    pid_t cpid = fork();
                    if (cpid < 0)
                    {
                        fprintf(stderr, "fork failed\n");
                        return 1;
                    }
                    else if (cpid == 0)
                    {
                        printf("Writer: [%s]\n", arg[0]);
                        dup2(fd[1], 1);
                        close(fd[0]);
                        close(fd[1]);
                        dump_args(getpid(), arg);
                        execvp(arg[0], arg);
                        fprintf(stderr, "Failed to exec %s\n", arg[0]);
                        exit(1);
                    }
                    else
                    {
                        printf("Reader: [%s]\n", arg[2]);
                        assert(cpid > 0);
                        dup2(fd[0], 0);
                        close(fd[0]);
                        close(fd[1]);
                        dump_args(getpid(), &arg[2]);
                        execvp(arg[2], &arg[2]);
                        fprintf(stderr, "Failed to exec %s\n", arg[2]);
                        exit(1);
                    }
                }
                else
                {
                    close(fd[0]);
                    close(fd[1]);
                    assert(pid > 0);
                    while (waitpid(pid, NULL, 0) != -1)
                        ;
                }
            }
        }
        return 1;
    }
    

    【讨论】:

      【解决方案2】:

      您使用的是fd[0]fd[2],但pipe(fd) 仅设置fd[0]fd[1]

      【讨论】:

      • 是的,我意识到这一点并将 fd[2] 更改为 fd[1] 但同样的错误仍然存​​在
      【解决方案3】:

      几个直接的问题:

      1. setup() 没有返回值,但您需要一个 int

      2. fgets的定义是:

        char * fgets ( char * str, int num, FILE * stream );
        

        从流中获取字符串
        stream 读取字符并将它们作为 C 字符串存储到 str 中,直到 (num-1) 字符被读取或到达换行符或文件结尾,以先发生者为准。

      换行符使fgets 停止读取,但该函数将其视为有效字符并包含在复制到str 的字符串中。

      fgets() 出错时返回 NULL;否则它返回一个指向str 的指针。因此,这在您的 while 循环中似乎是一个非常不可靠的测试条件。

      【讨论】:

      • 您对fgets() 的定义在某些细节上与fgets() 的标准C 定义不同。另外,我不清楚您的最后一段试图声明什么:fgets() 在 EOF 或错误时返回 NULL — 而while (fgets(line, sizeof(line), fp) != NULL) 是读取 EOF 的循环的一个很好的习惯用法。
      猜你喜欢
      • 2012-09-22
      • 1970-01-01
      • 2011-04-18
      • 2011-09-10
      • 2017-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-03
      相关资源
      最近更新 更多