一尘不染

Linux非阻塞FIFO(按需记录)

linux

我喜欢记录“按需”输出的程序。例如。输出记录到终端,但是另一个进程可以随时挂接到当前输出。

经典方法是:

myprogram 2>&1 | tee /tmp/mylog

并按需

tail /tmp/mylog

但是,这将创建一个不断增长的日志文件,即使在驱动器空间不足之前也不会使用它。所以我的尝试是:

mkfifo /tmp/mylog
myprogram 2>&1 | tee /tmp/mylog

并按需

cat /tmp/mylog

现在,我可以随时阅读/ tmp / mylog。但是,任何输出都会阻止程序,直到读取/ tmp /
mylog。我喜欢fifo刷新所有不回读的传入数据。怎么做?


阅读 561

收藏
2020-06-02

共1个答案

一尘不染

受您的问题启发,我编写了一个简单的程序,可以让您执行以下操作:

$ myprogram 2>&1 | ftee /tmp/mylog

它的行为类似于,tee但将stdin克隆到stdout和命名管道(现在是必需的)而没有阻塞。这意味着,如果您想以这种方式记录日志,则可能会丢失日志数据,但是我想这在您的情况下是可以接受的。诀窍是阻止SIGPIPE信号并忽略写入损坏的fifo时的错误。当然,可以通过各种方式优化此示例,但是到目前为止,我猜它已经完成了工作。

/* ftee - clone stdin to stdout and to a named pipe 
(c) racic@stackoverflow
WTFPL Licence */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int readfd, writefd;
    struct stat status;
    char *fifonam;
    char buffer[BUFSIZ];
    ssize_t bytes;

    signal(SIGPIPE, SIG_IGN);

    if(2!=argc)
    {
        printf("Usage:\n someprog 2>&1 | %s FIFO\n FIFO - path to a"
            " named pipe, required argument\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    fifonam = argv[1];

    readfd = open(fifonam, O_RDONLY | O_NONBLOCK);
    if(-1==readfd)
    {
        perror("ftee: readfd: open()");
        exit(EXIT_FAILURE);
    }

    if(-1==fstat(readfd, &status))
    {
        perror("ftee: fstat");
        close(readfd);
        exit(EXIT_FAILURE);
    }

    if(!S_ISFIFO(status.st_mode))
    {
        printf("ftee: %s in not a fifo!\n", fifonam);
        close(readfd);
        exit(EXIT_FAILURE);
    }

    writefd = open(fifonam, O_WRONLY | O_NONBLOCK);
    if(-1==writefd)
    {
        perror("ftee: writefd: open()");
        close(readfd);
        exit(EXIT_FAILURE);
    }

    close(readfd);

    while(1)
    {
        bytes = read(STDIN_FILENO, buffer, sizeof(buffer));
        if (bytes < 0 && errno == EINTR)
            continue;
        if (bytes <= 0)
            break;

        bytes = write(STDOUT_FILENO, buffer, bytes);
        if(-1==bytes)
            perror("ftee: writing to stdout");
        bytes = write(writefd, buffer, bytes);
        if(-1==bytes);//Ignoring the errors
    }
    close(writefd); 
    return(0);
}

您可以使用以下标准命令进行编译:

$ gcc ftee.c -o ftee

您可以通过运行以下命令快速进行验证:

$ ping www.google.com | ftee /tmp/mylog

$ cat /tmp/mylog

另请注意-这不是多路复用器。一次只能执行一个进程$ cat /tmp/mylog

2020-06-02