如何运行另一个程序,通过stdin传递数据,并在Linux中获取其stdout输出?

问题描述 投票:0回答:2

基本上我想在C中实现这样的东西: output=$(echo -n 1234 | md5sum) 1234是输入,md5sum是我想要运行的程序。

我已经检查了一些SO问题,但似乎没有一个正是我正在寻找的,或者他们没有提供我可以测试的完整代码示例。

我已经尝试过popen并能够读取或写入另一个程序,但显然它不是双向的。

一些帖子似乎暗示pipedup2或其他一些功能,但他们没有解释如何使它们工作。

c linux pipe
2个回答
1
投票

popen() could be used

主要问题是它想要实现相当于shell命令:

output=$(echo -n 1234 | md5sum)

这可以用C语言写成popen()

#include <stdio.h>

int main(void)
{
    char output[64];
    FILE *fp = popen("echo -n 1234 | md5sum", "r");
    if (fp == 0)
    {
        fprintf(stderr, "failed to create pipeline\n");
        return 1;
    }
    size_t nbytes = fread(output, sizeof(char), sizeof(output), fp);
    pclose(fp);
    if (nbytes > 0)
    {
        printf("output=[%.*s]\n", (int)nbytes, output);
        return 0;
    }
    else
    {
        printf("no output available!\n");
        return 1;
    }
}

这个输出是:

output=[4c35fa2227e11ba8c892cbbb5d46417c  -
]

请注意,这相当于以下第一个命令的输出:

$ printf "%s %s\n" -n 1234 | md5sum
4c35fa2227e11ba8c892cbbb5d46417c  -
$ printf "%s" 1234 | md5sum
81dc9bdb52d04dc20036dbd8313ed055  -
$

(使用echo -n并不是完全可移植的。有空间认为你期待第二个命令的输出。)

If you want two-way communication

但是,标题中的标题问题表明您希望程序将数据写入md5sum命令并再次读取响应哈希值。这当然是可行的,但你必须更加努力,popen()不再合适。我懒得不使用我首选的错误报告功能,这些功能在我的SOQ — Stack Overflow Questions存储库中的GitHub上可用作stderr.c目录中的文件stderr.hLibSOQ。当您只是编写测试并在测试指示失败时进行函数调用时,它使错误检查变得更加简单。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "stderr.h"

static void fd_copy(int ifd, int ofd)
{
    char buffer[1024];
    ssize_t rbytes;
    while ((rbytes = read(ifd, buffer, sizeof(buffer))) > 0)
    {
        ssize_t wbytes;
        if ((wbytes = write(ofd,buffer, (size_t)rbytes)) != rbytes)
            err_syserr("short write %zd bytes instead of %zd expected: ", wbytes, rbytes);
    }
}

static void be_childish(int *to_child, int *to_parent)
{
    if (dup2(to_child[0], STDIN_FILENO) != STDIN_FILENO)
        err_syserr("failed to duplicate pipe to standard input: ");
    if (dup2(to_parent[1], STDOUT_FILENO) != STDOUT_FILENO)
        err_syserr("failed to duplicate pipe to standard output: ");
    close(to_child[1]);
    close(to_child[1]);
    close(to_parent[0]);
    close(to_parent[0]);
    char *cmd[] = { "md5sum", 0 };
    execvp(cmd[0], cmd);
    err_syserr("failed to execute command %s: ", cmd[0]);
    /*NOTREACHED*/
}

static void be_parental(const char *fname, int *to_child, int *to_parent)
{
    close(to_child[0]);
    close(to_parent[1]);
    int fd = open(fname, O_RDONLY);
    if (fd < 0) 
        err_syserr("failed to open file '%s' for reading: ", fname);
    fd_copy(fd, to_child[1]);
    close(fd);
    close(to_child[1]);

    char buffer[128];
    ssize_t rbytes = read(to_parent[0], buffer, sizeof(buffer));
    close(to_parent[0]);
    if (rbytes <= 0)
        err_syserr("read error (or unexpected EOF) from hash process: ");
    buffer[strspn(buffer, "0123456789abcdefABCDEF")] = '\0';
    printf("%s: MD5 %s\n", fname, buffer);
}

static void md5hash(const char *fname)
{
    int to_child[2];
    int to_parent[2];
    if (pipe(to_child) != 0 || pipe(to_parent) != 0)
        err_syserr("failed to create 2 pipes: ");
    int pid = fork();
    if (pid < 0)
        err_syserr("failed to fork: ");
    if (pid == 0)
        be_childish(to_child, to_parent);
    else
        be_parental(fname, to_child, to_parent);
}

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    if (argc <= 1)
        err_usage("file [...]");

    for (int i = 1; i < argc; i++)
        md5hash(argv[i]);
    return 0;
}

示例输出(程序是pop73):

$ pop73 mm*
mm19.c: MD5 1d2207adec878f8f10d12e1ffb8bcc4b
mm23.c: MD5 c3948c5a80107fdbfac9ad755e7e6470
mm53.c: MD5 a0a24610400d900eb18408519678481e
mm59.c: MD5 1a5b1807c331dd1b5b6ce5f6ffed7c59
$ md5sum mm*
1d2207adec878f8f10d12e1ffb8bcc4b  mm19.c
c3948c5a80107fdbfac9ad755e7e6470  mm23.c
a0a24610400d900eb18408519678481e  mm53.c
1a5b1807c331dd1b5b6ce5f6ffed7c59  mm59.c
$

这确认文件已正确复制到子项,输出MD5哈希匹配。

我喜欢涉及子进程和父进程的程序的be_childish()be_parental()函数名称。使用离散作业的函数使代码在长期内更易于理解和维护。另请注意,代码关闭文件描述符的注意事项。

经验法则:如果将管道的一端复制到标准输入或标准输出(或标准错误),则应在继续之前关闭管道的两端。

be_childish()函数举例说明了经验法则。在继续执行命令之前,它会关闭它传递的2个管道的所有4个描述符,因为它已将每个管道中的一个描述符复制到其中一个标准流。

请注意:如果不将文件描述符复制到其中一个标准通道,则通常不会立即关闭管道的两个文件描述符。您通常会关闭管道的一端 - 当前进程不会使用的末端。但是另一个是开放的,直到你完成数据处理,无论这意味着什么。 be_parental()函数举例说明了推论。


0
投票

为您的输入和输出提供一些测试用例,您想用echo -n实现的只有n位数或其他什么?通过一些假设,我尝试了下面。

xyz@xyz-PC:~/s_flow$ cat m5sum.c 
#include<stdio.h>
int main()
{
    int n;
    printf("enter n :\n");
    scanf("%d",&n);
    printf("n = %d \n",n);
    return 0;
}
xyz@xyz-PC:~/s_flow$ echo  12345 | ./m5sum
enter n :
n = 12345

你想要输出如上所述我们如何使用echo?

echo  12345 | ./m5sum 
   |             |        => process1 output will be input for process 2 
process1       process2 

首先你需要一个pipe进行写作和阅读,条件是process1不应该在屏幕上显示输出(stdout)所以为此你应该close(1)&这应该输入process2即过程2不应该从用户读取(stdinclose(0)也是如此。

这是我的建议

int main()
{
        int p[2];
        pipe(p);
        if(fork()==0)//child
        {
                close(0);//closing std in
                dup(p[0]);//duplicating read end of pipe
                close(p[1]);//closing write end of pipe
                execlp("process1_cmd","cmd_option",NULL);
        }
        else
        {
                close(1);//close stdout
                dup(p[1]);// duplicating write end of pipe
                close(p[0]);//closing read end of pipe  
                execlp("process_2","process_2",NULL);
        }
}

process_1和process_2可以是任何预定义/用户定义的输入命令。我希望它有助于实现你的目标。

© www.soinside.com 2019 - 2024. All rights reserved.