Паралельно обробка обмеженої кількості команд Bash script


196

У мене є сценарій bash, який виглядає приблизно так:

#!/bin/bash
wget LINK1 >/dev/null 2>&1
wget LINK2 >/dev/null 2>&1
wget LINK3 >/dev/null 2>&1
wget LINK4 >/dev/null 2>&1
# ..
# ..
wget LINK4000 >/dev/null 2>&1

Але обробка кожного рядка до тих пір, поки команда не буде закінчена, а потім перехід до наступного дуже трудомісткий, я хочу обробити, наприклад, 20 рядків одразу, потім, коли вони закінчені, ще 20 рядків обробляються.

Я думав над тим, wget LINK1 >/dev/null 2>&1 &щоб відправити команду на другий план і продовжувати, але тут є 4000 рядків, це означає, що у мене виникнуть проблеми з продуктивністю, не кажучи вже про обмеження в тому, скільки процесів я повинен запустити одночасно, так що це не добре ідея.

Одне рішення, про яке я зараз думаю, - це перевірити, чи працює одна з команд чи ні, наприклад, через 20 рядків я можу додати цю петлю:

while [  $(ps -ef | grep KEYWORD | grep -v grep | wc -l) -gt 0 ]; do
sleep 1
done

Звичайно, у цьому випадку мені потрібно буде додати & до кінця рядка! Але я відчуваю, що це не правильний спосіб зробити це.

Отже, як я насправді групую кожні 20 рядків разом і чекаю, коли вони закінчать, перш ніж перейти до наступних 20 рядків, цей сценарій динамічно генерується, щоб я міг робити все, що я хочу на ньому, поки він генерується, але це НЕ потрібно Використовуйте wget, це був лише приклад, тому будь-яке рішення, яке є специфічним для wget, не принесе мені користі.


1
waitтут є правильною відповіддю, але вам while [ $(ps …було б набагато краще писати while pkill -0 $KEYWORD…- використовуючи proctools ... тобто з законних причин перевірити, чи не працює процес із певним іменем.
kojiro

Я думаю, що це питання слід знову відкрити. "Можливий дублікат" QA - це паралельне виконання обмеженої кількості програм. Як і 2-3 команди. Це питання, однак, зосереджено на виконанні команд, наприклад, в циклі. (див. "але є 4000 рядків").
ВасиліНовіков

@VasyaNovikov Чи прочитали ви всі відповіді і на це запитання, і на дублікат? Кожна відповідь на це запитання тут, також можна знайти у відповідях на повторне запитання. Це саме визначення дублюючого питання. Це абсолютно не має ніякої різниці в тому, чи виконуєте ви команди в циклі чи ні.
robinCTS

@robinCTS є перехрестя, але самі питання різні. Також 6 найпопулярніших відповідей на пов'язаний QA стосуються лише двох процесів.
ВасильНовіков

2
Я рекомендую повторно відкрити це питання, оскільки його відповідь чіткіша, чіткіша, краща та набагато більш прихильна, ніж відповідь на пов’язане питання, хоча це на три роки пізніше.
Дан Ніссенбаум

Відповіді:


331

Використовуйте waitвбудований:

process1 &
process2 &
process3 &
process4 &
wait
process5 &
process6 &
process7 &
process8 &
wait

У наведеному вище прикладі, 4 процеси process1... process4будуть розпочаті у фоновому режимі, і оболонка зачекає, поки вони завершаться, перш ніж розпочати наступний набір.

З посібника GNU :

wait [jobspec or pid ...]

Зачекайте, поки дочірній процес, визначений кожним підписом ідентифікатора процесу або специфікацією завдання jobspec, завершиться, і поверніть статус виходу останньої очікуваної команди. Якщо специфікація задана, всі процеси в роботі чекають. Якщо ніяких аргументів не наводиться, чекають усі поточно активні дочірні процеси, а стан повернення дорівнює нулю. Якщо ні jobspec, ні pid не визначають активний дочірній процес оболонки, стан повернення - 127.


14
Так в основномуi=0; waitevery=4; for link in "${links[@]}"; do wget "$link" & (( i++%waitevery==0 )) && wait; done >/dev/null 2>&1
якийro

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

1
Чи є спосіб це зробити в циклі?
DomainsFeatured

Я спробував це, але здається, що призначення змінних, виконані в одному блоці, недоступні в наступному блоці. Це тому, що це окремі процеси? Чи є спосіб передати змінні до основного процесу?
Боббі

97

Дивіться паралельно . Його синтаксис схожий на xargs, але він виконує команди паралельно.


13
Це краще, ніж використовувати wait, оскільки це турбується про початок нових завдань у міру завершення старих, замість того, щоб чекати, поки ціла партія закінчиться перед початком наступної.
чепнер

5
Наприклад, якщо у вас є список посилань у файлі, ви можете зробити це, cat list_of_links.txt | parallel -j 4 wget {}яке триватиме чотири wgets одночасно.
Містер Лама

5
У місті є нова дитина під назвою pexec, яка замінює її parallel.
косоокість

2
Надання прикладу було б кориснішим
jterm

1
parallel --jobs 4 < list_of_commands.sh, де list_of_commands.sh - це файл з однією командою (наприклад wget LINK1, примітка без цього &) у кожному рядку. Може знадобитися CTRL+Zі bgпісля, щоб залишити його працювати у фоновому режимі.
weiji14

71

Насправді, xargs можна паралельно виконувати команди для вас. Для цього існує спеціальний -P max_procsваріант командного рядка. Див man xargs.


2
+100 це чудово, оскільки він вбудований і дуже простий у використанні і може бути виконаний в одній
Clay

Чудово використовувати для невеликих контейнерів, оскільки додаткові пакети / залежності не потрібні!
Марко Рой

1
Дивіться це питання для прикладів: stackoverflow.com/questions/28357997/…
Marco Roy

7

Ви можете запустити 20 процесів і використовувати команду:

wait

Ваш сценарій буде чекати і продовжуватися, коли всі ваші фонові завдання будуть закінчені.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.