Как запустить несколько параллельных заданий оболочки и контролировать их все одновременно?

Как запустить несколько параллельных заданий оболочки и контролировать их все одновременно?

У меня есть большое количество больших файлов (сотни файлов, сотни МБ каждый), которые мне нужно пропустить через ряд программ для их фильтрации и преобразования. Я использую преимущества нескольких ядер ЦП, поэтому я запускаю несколько экземпляров одного и того же канала для каждого файла (может быть до сотни ядер, и могу использовать ssh как часть канала, если это имеет какое-либо значение для ответа). Я хочу контролировать каждый канал, и я использую pvдля этого. Вот минимальный пример того, что у меня есть:

$ pv file-001.gz | gunzip | xz > file-001.xz
1.58GB 0:00:02 [ 713MB/s] [=================================>] 100%

На самом деле я делаю в канале еще несколько вещей, включая передачу данных на другие машины по ssh и их прохождение через фильтры на этих машинах, но канал всегда заканчивается перенаправлением в новый файл на главном хосте. Кроме того, ни один этап в канале не требует всего набора данных; они могут работать построчно или по частям.

В настоящее время мне нужно отдельное окно терминала для каждого экземпляра конвейера. Я хотел бы запустить n параллельных экземпляров конвейера в одном терминале/оболочке и получить вывод от каждого экземпляра pv на его собственной строке. Что-то вроде этого:

1.48GB 0:00:54 [ 704MB/s] [===============================>  ]  97% ETA 00:00:06
1.58GB 0:01:00 [ 713MB/s] [=================================>] 100%
0.75GB 0:00:31 [ 709MB/s] [================>                 ]  50% ETA 00:00:29

Значение n будет числом строк, которые я могу поместить в окне терминала, скажем, 3-50 или около того. Точный формат отчета о ходе выполнения не важен, если он включает скорость, процент выполнения, прошедшее время и предполагаемое оставшееся время. Также не важно, что я использую pv, можно использовать какую-то другую программу, если я могу легко ее установить или просто оболочку (предпочтительно bash). Однако важно то, что метод может обрабатывать случайные поломки конвейера в случае, если часть конвейера по какой-то причине выходит из строя. Я также хотел бы начинать новые задания каждый раз, когда задание завершается (успешно или нет), и все еще остаются необработанные файлы.

Есть идеи, как это сделать?

Обратите внимание, что я уже пробовалGNU-параллельный, но его функции ssh, по-видимому, предполагают, что каждый входной файл сначала передается на удаленный хост, затем обрабатывается, а затем результат передается обратно, чего я хочу избежать из-за объема вовлеченных данных и ограниченного объема пространства на каждом узле обработки.

решение1

Есть идеи, как это сделать?

Нет.

pv имеет опции -c и -N, которые позволят вам делать то, что вы хотите

$ pv -cN source access.log | gzip | pv -cN gzip > access.log.gz
source:  760MB 0:00:15 [37.4MB/s] [=>     ] 19% ETA 0:01:02
  gzip: 34.5MB 0:00:15 [1.74MB/s] [  <=>  ]

но я не вижу, как применить эту функцию к нескольким конвейерам


Однако, если вы посмотрите на страницу руководства pv, вы увидите следующее:

          (tar cf - . \
           | pv -n -s $(du -sb . | awk '{print $1}') \
           | gzip -9 > out.tgz) 2>&1 \
          | dialog --gauge 'Progress' 7 70

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

В настоящее время мне нужно отдельное окно терминала для каждого экземпляра трубы.

Моя главная мысль заключается в том, что вам не обязательно интерактивно открывать множество окон терминала, вы можете сделать так, чтобы один скрипт сам открывал множество диалоговых окон.


решение2

Вы искали --pipeGNU Parallel?

cat bigfiles* | pv | parallel --pipe -S server1,server2 'cat | process_pipe'

(кот включен для выразительности)

По умолчанию размер блока составляет 1 МБ, его можно изменить с помощью --block.

-- редактировать для личной переписки --

На основании вышеизложенного можно получить 1-1 переписку следующим образом:

parallel --eta "cat {} | parallel --pipe -S server1,server2 'cat | process_pipe' > {}.out" ::: bigfiles*

(кот включен для выразительности)

Это не совсем оптимально, так как внутренний параллелизм не будет знать о своих братьях и сестрах и, таким образом, может порождать больше на server1, чем на server2. Один из способов избежать этого - -j1 на внешнем параллелизме, но это не будет оптимальным, если внутренний имеет достаточно блоков только для первого сервера. Другими словами: чтобы идеально сбалансировать вашу рабочую нагрузку, вам, возможно, придется немного повозиться с этим - возможно, даже использовать --load 100% или что-то подобное.

--- правка: Борьба со сбоями ---

Если process_pipeвозвращается ошибка, то следует повторить команду еще 2 раза:

parallel --retries 3 --eta "cat {} | parallel --pipe -S server1,server2 'cat | process_pipe' > {}.out" ::: bigfiles*

Связанный контент