Хотя сделать это правильно, bashвероятно, невозможно, вы можете довольно легко сделать полуправо. bstarkдал хорошее приближение к праву, но у него есть следующие недостатки:
- Разделение слов: вы не можете передавать ему задания, в аргументах которых используются следующие символы: пробелы, табуляции, новые строки, звездочки, вопросительные знаки. Если вы это сделаете, все сломается, возможно, неожиданно.
- Он полагается на то, что остальная часть вашего скрипта ничего не создает в фоновом режиме. Если вы это сделаете или позже вы добавите что-то в сценарий, который будет отправлен в фоновом режиме, потому что вы забыли, что вам не разрешено использовать фоновые задания из-за его фрагмента, все сломается.
Еще одно приближение, не имеющее этих недостатков:
scheduleAll() {
local job i=0 max=4 pids=()
for job; do
(( ++i % max == 0 )) && {
wait "${pids[@]}"
pids=()
}
bash -c "$job" & pids+=("$!")
done
wait "${pids[@]}"
}
Обратите внимание, что этот легко адаптируется, чтобы также проверять код выхода каждого задания по мере его завершения, поэтому вы можете предупреждать пользователя, если задание не удается, или установить код выхода в scheduleAllсоответствии с количеством заданий, которые не удалось выполнить, или что-то еще.
Проблема с этим кодом как раз в том, что:
- Он планирует четыре (в данном случае) задания одновременно, а затем ожидает завершения всех четырех. Некоторые из них могут быть выполнены раньше, чем другие, что приведет к тому, что следующий пакет из четырех заданий будет ждать, пока не будет выполнено самое длинное из предыдущего пакета.
Решение, которое решает эту последнюю проблему, должно использовать kill -0 для опроса, исчезли ли какие-либо процессы вместоwait запланировать следующее задание. Однако это создает небольшую новую проблему: у вас есть состояние гонки между завершением задания и kill -0проверкой того, закончилось ли оно. Если задание завершилось и в то же время запускается другой процесс в вашей системе, принимая случайный PID, который является идентификатором только что завершенного задания, kill -0он не заметит, что ваша работа завершена, и все снова сломается.
Идеальное решение невозможно в bash.