Kết hợp đầu vào từ nhiều tập tin / đường ống mà không bị tắc nghẽn hoặc chặn?


9

Có một công cụ sẽ lấy đầu vào từ nhiều tệp hoặc đường ống và ghi nó vào thiết bị xuất chuẩn, mà không chặn các lần đọc, sao cho các dòng đầu vào riêng lẻ xuất hiện nguyên vẹn? Về cơ bản, tôi muốn ghép một loạt các đầu vào vào một đầu ra mà không có các dòng ghi đè.

$ combine file1 <(prog2) ... > nice-output.txt
  1. Tôi không quan tâm đến thứ tự đầu ra
  2. Nó không nên chặn miễn là một số đầu vào có dữ liệu
  3. Nó sẽ hiệu quả (nghĩa là tôi có thể hạ cấp Perl one-liner của bạn;)

Câu trả lời:


4

Bạn sẽ có thể làm điều này với multitailkhá dễ dàng.


1
Bạn có thể đề xuất những đối số nào tôi sẽ sử dụng với multitail không? Nó dường như không có chế độ không tương tác, treo cố gắng ghi vào thiết bị xuất chuẩn và đọc lỗi từ một đường ống.
Jay Hacker

Bắt đầu với -Lđể chạy một lệnh và hợp nhất đầu ra với luồng hiện tại và -ađể ghi đầu ra vào một tệp. Tôi sẽ tìm kiếm nhiều hơn vào ngày mai. Nếu bạn đưa ra một ví dụ chi tiết hơn, tôi sẽ cố gắng làm việc.
Caleb

4

Nếu các quy trình viết các dòng trong một writecuộc gọi, yêu cầu các quy trình sử dụng bộ đệm dòng (thường bị tắt nếu đầu ra tiêu chuẩn của chúng không phải là một thiết bị đầu cuối), bạn có thể chỉ tất cả chúng vào một đường ống.

{ { sleep .1; echo one; sleep .1; echo two; } &
  { echo hello; sleep .15; echo world; };
  wait; } | cat

Nếu các quy trình chỉ thực hiện đệm dòng khi ghi vào thiết bị đầu cuối, cách dễ dàng là sử dụng script. Đó là một chút vụng về: nó chỉ có thể ghi vào một tập tin.

script -q -c '
    { { sleep .1; echo one; sleep .1; echo two; } &
      { echo hello; sleep .15; echo world; };
      wait; }'
tail -n +2 typescript

Nếu các chương trình viết các dòng dài hoặc chỉ không sử dụng bộ đệm dòng, phương pháp này sẽ không hiệu quả. Bạn sẽ cần một chương trình thu thập để đọc và đệm các dòng từ mỗi đầu vào một cách riêng biệt và thực hiện đồng bộ hóa trên các kết thúc dòng. Không có tiện ích tiêu chuẩn với chức năng này. Tôi đề nghịmultitail thứ hai của Caleb về .

Đây là một tập lệnh Python đọc các dòng được tạo bởi một số lệnh và nhổ chúng ra trên đầu ra tiêu chuẩn của nó, mà không phá vỡ một dòng. Tôi đã không kiểm tra nó nhiều, vì vậy hãy cẩn thận. Tôi đã không điểm chuẩn ở tất cả.

#!/usr/bin/env python
import Queue, itertools, os, subprocess, sys, threading
# Queue of (producer_id, line). line==None indicates the end of a producer.
lq = Queue.Queue()

# Line producer
def run_task(i, cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
    line = p.stdout.readline()
    while line <> "":
        lq.put((i, line))
        line = p.stdout.readline()
    lq.put((i, None))

# Start a producer for each command passed as an argument
for i in range(1,len(sys.argv)):
    threading.Thread(target=run_task, args=(i, sys.argv[i])).start()
sources = len(sys.argv) - 1
# Consumer: print lines as they come in, until no producer is left.
while sources > 0:
    (k, line) = lq.get()
    if line == None: sources -= 1
    else: sys.stdout.write(str(k) + ":" + line)

Sử dụng mẫu:

./collect.py 'sleep 1; ls /; sleep 1; ls /' \
             '/bin/echo -n foo; sleep 1; /bin/echo -n bar; sleep 1; /bin/echo qux'

1

Yeah multitail dường như gắn liền với khái niệm "cửa sổ" như là một tập hợp con của một thiết bị đầu cuối; Tôi không thể làm cho nó chơi tốt như một thành phần đường ống.

Vì vậy, trông giống như chúng ta hafta làm mình này vết nứt đốt ngón tay

/* Copyright © 2015 sqweek@gmail.com
** Use/modify as you see fit but leave this attribution.
** If you change the interface and want to distribute the
** result please change the binary name too! */
#include <err.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>

/* typedefs are for pussies */
struct {
    char *filename; /* for clarity of errors */
    char *data;
    long len;
    long cap;
} saved[FD_SETSIZE] = {0};

void
ewriten(int fd, char *buf, int n)
{
    int done = 0, c;
    while (done < n) {
        if ((c=write(fd, buf + done, n - done)) <= 0 && errno != EINTR) {
            err(1, "write");
        }
        done += c;
    }
}

int
empty(fd_set *fdset, int maxfd)
{
    int i;
    for (i=0; i <= maxfd; i++) {
        if (FD_ISSET(i, fdset)) return 0;
    }
    return 1;
}

void
combine(fd_set *fdset, int maxfd)
{
    char buf[4096], *cp;
    fd_set ready;
    int n, i, fd, left;
    while (!empty(fdset, maxfd)) {
        ready = *fdset;
        /* timeouts are for pussies */
        if (select(maxfd + 1, &ready, NULL, NULL, NULL) == -1) err(1, "select");
        for (fd=0; fd <= maxfd; fd++) {
            if (!FD_ISSET(fd, &ready)) continue;

            switch (n=read(fd, &buf, sizeof(buf))) {
            case -1:
                if (errno == EINTR)
                    break; /* ignore interrupts; we'll re-read next iteration */
                if (saved[fd].filename) err(1, "read: %s", saved[fd].filename);
                err(1, "read: %d", fd);
            case 0:
                if (saved[fd].len > 0) {
                    /* someone forgot their newline at EOF... */
                    ewriten(1, saved[fd].data, saved[fd].len);
                    saved[fd].data[0] = '\n'; /* put it back for them */
                    ewriten(1, saved[fd].data, 1);
                }
                free(saved[fd].data);
                FD_CLR(fd, fdset);
                break;
            default:
                for (cp=buf + n - 1; cp >= buf && *cp != '\n'; cp--); /* find last newline */
                left = n - (cp - buf + 1);
                if (cp >= buf) {
                    /* we found one! first dump any saved data from the last read */
                    if (saved[fd].len > 0) {
                        ewriten(1, saved[fd].data, saved[fd].len);
                        saved[fd].len = 0;
                    }
                    ewriten(1, buf, cp - buf + 1);
                }
                if (left > 0) {
                    /* now save any leftover data for later */
                    int need = saved[fd].len + left;
                    if (saved[fd].cap < need &&
                       (saved[fd].data=realloc(saved[fd].data, need)) == NULL) {
                        errx(1, "realloc: failed on %d bytes", need);
                        /* it was good enough for quake... */
                    }
                    saved[fd].cap = need;
                    memcpy(saved[fd].data + saved[fd].len, buf + n - 1 - left, left);
                    saved[fd].len += left;
                }
            }
        }
    }
}

void
addfd(int fd, fd_set *fdset, int *maxfd)
{
    FD_SET(fd, fdset);
    if (*maxfd < fd) {
        *maxfd = fd;
    }
}

int
main(int argc, char **argv)
{
    fd_set fdset;
    char **arg = argv + 1;
    char *cp;
    struct stat st;
    int fd, maxfd = -1;
    FD_ZERO(&fdset);
    while (*arg != NULL) {
        /* getopt is for pussies */
        if (strncmp("-u", *arg, 2) == 0) {
            *arg += 2;
            if (**arg == '\0' && *++arg == NULL ) errx(1, "-u requires argument (comma separated FD list)");
            /* reentrancy is for pussies */
            for (cp=strtok(*arg, ","); cp != NULL; cp=strtok(NULL, ",")) {
                fd = atoi(cp);
                if (fstat(fd, &st) != 0) err(1, "%d", fd);
                addfd(fd, &fdset, &maxfd);
            }
            arg++;
        } else if (strcmp("-", *arg) == 0) {
            if (fstat(0, &st) != 0) err(1, "stdin", fd);
            addfd(0, &fdset, &maxfd);
            saved[0].filename = "stdin";
            arg++;
        } else if (strcmp("--", *arg) == 0) {
            arg++;
            break;
        } else if (**arg == '-') {
            errx(1, "unrecognized argument %s", *arg);
        } else {
            break; /* treat as filename */
        }
    }
    /* remaining args are filenames */
    for (; *arg != NULL; arg++) {
        /* stdio is for pussies */
        if ((fd=open(*arg, O_RDONLY)) == -1) err(1, "open: %s", *arg);
        addfd(fd, &fdset, &maxfd);
        saved[fd].filename = *arg;
    }
    combine(&fdset, maxfd);
    return 0;
}

Ahhh mà cảm thấy tốt.

(lưu ý: nó đã được thử nghiệm trên khoảng hai bộ đầu vào. lỗi có thể tồn tại hoặc không tồn tại)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.