Группировка строк в bash до максимального количества строк

#bash #gnu-parallel

#bash #gnu-параллельный

Вопрос:

Проблема заключается в следующем:

У меня есть команда, которая выводит данные в строках со случайными таймингами

 ./cmd1
t0: a
t1: b
t2: c
t3: d
t4: e
t5: f
...
 

и я хочу обработать эти строки второй командой cmd2 .

Прямо сейчас я использую gnu parallel.

./cmd1 | parallel cmd2

который будет вызываться cmd2 один раз на строку.

Однако я хотел бы пакетировать выходные данные, сгенерированные cmd1 таким образом, чтобы:

  • До N строк отправляются в cmd2
  • Группировка завершится через T секунд и cmd2 будет вызываться со 0 < n < N строками.

Например, при T = 1 и N = 2

cmd1 вывод:

временная метка значение
0 a
0.1 b
0.2 c
1.3 d
1.4 e

будут появляться рабочие места:

временная метка значение
0.1 cmd2 a b # вызывается, потому что там было 2 строки
1.1 cmd2 c # вызывается из-за истечения времени ожидания группировки
1.4 cmd2 d e

Ответ №1:

Это довольно легко сделать tcl благодаря циклу событий. Настройте один обратный вызов, когда стандартный ввод может быть прочитан для обработки строки, и периодический, который вызывается каждые T секунд для очистки буферизованных строк, если это необходимо.

 #!/usr/bin/env tclsh

if {[llength $argv] != 3} {
    puts stderr "Usage: $argv0 linecount timeout command"
    exit 1
}
lassign $argv nlines timeout cmd
set timeout [expr {$timeout * 1000}]

set buffer {}
proc flush_buffer {} {
    # Invoke the command with the buffered lines (if any) as arguments
    global buffer cmd
    if {[llength $buffer]} {
        exec $cmd {*}$buffer amp;
        set buffer {}
    }
}

proc handle_timeout {} {
    # On timeout, flush any buffered lines and requeue.
    global timeout
    flush_buffer
    after $timeout handle_timeout
}

proc read_line {fd} {
    # Read a line from a channel, buffer it, and flush buffer if full.
    global buffer nlines
    if {[gets $fd line] >= 0} {
        lappend buffer $line
        if {[llength $buffer] >= $nlines} {
            flush_buffer
        }
    } elseif {[chan eof $fd]} {
        # Nothing left to read
        global forever
        flush_buffer
        set forever 1
    }
}

# Set up event handlers
after $timeout handle_timeout
chan configure stdin -blocking 0
chan event stdin readable {read_line stdin}

# And run
vwait forever
 

Пример, с парой сценариев оболочки, которые выдают строки с интервалами, а другой — просто повторяет аргументы командной строки:

 $ ./spew.sh | ./grouper 2 1 ./printer.sh
Starting printer process
(6565) line 1
(6565) line 2
Starting printer process
(6566) line 3
Starting printer process
(6568) line 4
(6568) line 5
Starting printer process
(6569) line 6
(6569) line 7