Вход объединения из нескольких файлов/каналов, не ударяя строки или блокирование?

Существует несколько различных шаблонов для опций, которые использовались исторически в приложениях UNIX. Несколько старых, как tar, используют позиционную схему:

аргументы опций команды

что касается использования tar в качестве примера

tar *something*f "файл, управляемый на" * "пути файлов для управления" *

В первой попытке избежать беспорядка, tar и несколько других программ со старым стилем аргументов флагов позволили разграничивать флаги с тире, но большинство из нас старые парни просто проигнорировало это.

Некоторые другие команды имеют более сложный синтаксис командной строки, как dd (1), который использует флаги, знаки "равно", пути, аргументы и куропатку в грушевом дереве, всех с диким отказом.

В BSD и более поздних версиях Unix, это более или менее сходилось к односимвольным флагам, отмеченным с '-', но это начало представлять несколько проблем:

  • флаги могло быть трудно помнить
  • иногда Вы на самом деле хотели использовать имя с '-'
  • и особенно с инструментами GNU, начали быть ограничения, наложенные количеством возможных флагов. Таким образом, инструменты GNU добавили GNU долгие опции как --output.

Затем Sun решил, что дополнительный '-' был избыточен и начал использовать флаги длинного стиля с единственным '-'s.

И это - то, как это стало путаницей, которая это теперь.

9
23.06.2011, 22:49
3 ответа

Необходимо смочь сделать это с multitail довольно легко.

4
27.01.2020, 20:07
  • 1
    Можно ли предложить, какие аргументы я использовал бы с мультихвостом? Это, кажется, не имеет неинтерактивный режим, подвешивает попытку записать в stdout и катастрофические отказы, читающие из канала. –  Jay Hacker 23.06.2011, 23:20
  • 2
    Запустите с -L выполнить команду и объединить вывод с текущим потоком и -a записать вывод в файл. Я посмотрю больше завтра. Если Вы дадите более подробный пример, то я попытаюсь работать он в. –  Caleb 23.06.2011, 23:23

Если процессы пишут строки на сингле write звоните, который требует, чтобы процессы использовали буферизацию строки (обычно выключаемый, если их стандартный вывод не является терминалом), можно просто указать на них всех на канал.

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

Если процессы только выполняют буферизацию строки при записи в терминал, простой способ состоит в том, чтобы использовать script. Это немного неуклюже: это может только записать в файл.

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

Если программы пишут длинные линии или просто не используют буферизацию строки, этот подход не собирается работать. Вам будет нужна программа коллектора, которая читает и буферизует строки от каждого входа отдельно и выполняет синхронизацию на окончаниях строки. Нет никакой стандартной утилиты с этой функциональностью. Я предложение второго Caleb multitail.

Вот сценарий Python, который читает линии, продолженные несколькими командами, и выкладывает их на его стандартном выводе, не разбивая строку. Я не протестировал его очень, таким образом, пользователь протеста. Я не сравнил его вообще.

#!/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)

Демонстрационное использование:

./collect.py 'sleep 1; ls /; sleep 1; ls /' \
             '/bin/echo -n foo; sleep 1; /bin/echo -n bar; sleep 1; /bin/echo qux'
4
27.01.2020, 20:07

Да, "мультихвост", похоже, связан с понятием "окно" как подмножество терминала; я не смог заставить его хорошо играть в качестве компонента трубопровода.

Похоже, мы должны сделать это сами трещит по костяшкам

/* 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;
}

Ааа, это было приятно.

(примечание: он протестирован примерно на двух наборах входов. багги могут существовать или не существовать)

1
27.01.2020, 20:07

Теги

Похожие вопросы