#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