向标准输出和文件同时写入内容

4

我有一个父进程,该进程分叉出子进程来执行execv()。

我需要将execv()输出到标准输出(stdout)并显示在屏幕上,同时将其复制到日志文件中。

如何将相同的输出写入stdout和文件中,而不使用管道(pipes)或tees


2
如您所写,您的问题会引起很多人的“使用tee”回复。考虑编辑以明确您希望在内部执行此操作... - dmckee --- ex-moderator kitten
这是一个与此相似但用法略有不同的广告复制链接:https://dev59.com/aHI-5IYBdhLWcg3wn525。尽管如此,我仍然认为我的回答最好。 - Martin York
@Ether,语言不同。 - user191776
7个回答

6

如果您不想使用Tee,可以在写入数据之前先将其写入文件,然后将其发送到标准输出。

您应该编写一个记录函数来为您完成此操作,使其更加简洁。


3

您可以完全在程序内部完成此操作,但仍需要使用匿名管道作为pipe()系统调用创建的。

基本上,您需要一个子进程执行与tee相当的操作,这很容易实现:

int child_pipe[2];
pid_t pid_exec_child, pid_output_child;

pipe(child_pipe);

pid_exec_child = fork();
if (pid_exec_child == 0)
{
    dup2(child_pipe[1], STDOUT_FILENO);
    close(child_pipe[0]);
    close(child_pipe[1]);
    execve(/* ... */);
    _exit(127);
}

close(child_pipe[1]);

pid_output_child = fork();
if (pid_output_child == 0)
{
    /* This child just loops around, reading from the other child and writing everything
     * to both stdout and the log file. */
    int logfd = open("logfile", O_WRONLY);
    char buffer[4096];
    ssize_t nread;

    while ((nread = read(child_pipe[0], buffer, sizeof buffer) != 0)
    {
        size_t nwritten_total;
        ssize_t nwritten;

        if (nread < 0)
        {
            if (errno == EINTR)
                continue;

            perror("read");
            _exit(1);
        }

        /* Copy data to stdout */
        nwritten_total = 0;
        while (nwritten_total < nread)
        {
            nwritten = write(STDOUT_FILENO, buffer + nwritten_total, nread - nwritten_total);

            if (nwritten < 0)
            {
                if (errno == EINTR)
                    continue;

                perror("write(stdout)");
                _exit(2);
            }

            nwritten_total += nwritten;
        }

        /* Copy data to logfile */
        nwritten_total = 0;
        while (nwritten_total < nread)
        {
            nwritten = write(logfd, buffer + nwritten_total, nread - nwritten_total);

            if (nwritten < 0)
            {
                if (errno == EINTR)
                    continue;

                perror("write(logfile)");
                _exit(3);
            }

            nwritten_total += nwritten;
        }
    }
    _exit(0);
}

close(child_pipe[0]);

/* Parent continues here */

当然,在第二个子进程中使用tee可能会更容易...(需要注意的是,子进程使用_exit(),因为它们从父进程继承了标准I/O状态)。

2
通过tee进行管道传输。

有没有一种方法可以不使用管道或者Tee,只使用dup()系列函数来完成这个操作? - user191776

2

2
那个链接提供了一个示例,其中stdout被重定向到一个文件,然后再次重定向回正常的stdout。发帖者想要找到一种方法,使得普通的打印语句能够同时输出到stdout和一个文件中。 - a'r
@Byron - fork 命令并非用于打印目的,它只是脚本逻辑的一部分,并包含一个示例。在该线程下方的帖子中,有一个不包含 fork 命令的纯逻辑解决方案:http://www.linuxforums.org/forum/linux-programming-scripting/69391-redirect-stdout-file.html#post363639 - DVK
1
正如所说,这并没有回答原始问题,即同时写入标准输出和文件。 - Antony Hatchkins

1

另外,你可以使用 fifo(命名管道)。 mkfifo my.fifo; 在 execv 中:程序 > my.fifo; 然后像普通文件一样打开 fifo 并从中读取。这样你就可以解析标准输出,但是它有共享访问的一些小缺点。


0
你想让子进程的输出只写入日志吗? Tee Unix命令正是你所描述的:你将数据导入,它会同时写入日志和标准输出。

0

像这样使用Tee

./myprog | tee outputfile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接