Привет коллега! Надеюсь ты выжил после выходных и не сорвал спину на мероприятии по копке картофеля. Сегодня у нас с тобой bash.



Мы прекрасно знаем, что bash скрипты выполняются последовательно. Но есть ли способы запускать команды параллельно?



Конечно есть, сейчас про это и поговорим. Покажу два способа как это можно легко осуществить. Первый способ основан на символе &.



Пишем подопытный скрипт:



#!/bin/bash



sleep 5 && echo "sleep 5_1" &

sleep 5 && echo "sleep 5_2" &

wait

echo "sleep 0"



Здесь первая и вторая команда запустятся параллельно. Через 5 секунд на экран выведутся 3 строчки sleep 5_1, sleep 5_2 и sleep 0.



Wait
– команда, которая ожидает завершения определенного процесса, а затем возвращает его состояние завершения.



Оболочка ждет (wait) пока завершатся два предыдущих процесса, прежде чем запустить следующую команду. Получается, все то, что в скрипте идет после wait, будет в режиме ожидания, пока не завершится первый sleep и второй.



Прикольно да? Открываются новые возможности для совершенствования своих костылей. Прям терпко попахивает асинхронностью, в хорошем смысле. Про wait поговорим в отдельном посте, есть там свои клевые фичи.



Теперь давай запустим в терминале последовательно такие команды:



sleep 60 &

sleep 90 &

sleep 120 &



На экран выведутся записи вроде таких:



[1] 38161

[2] 38166

[3] 38167



Это PID процессов которые ты запустил в фоновом режиме. Но интересует нас тут другое. А именно команда jobs.



Команда jobs в Linux позволяет пользователю напрямую взаимодействовать с процессами в текущей оболочке. Команда отображает состояние заданий в текущем сеансе.



Запускаем jobs и смотрим:



[1]   Done    sleep 60

[2]- Running sleep 90 &

[3]+ Running sleep 120 &



Опа! А это список команд, которые мы запустили в фоне. Первое задание завершилось, остальное еще шуршит. Некий мониторинг из коробки.



Давай теперь напишем более наглядный пример, где всё это можно применить.



downloader(){

wget -q "$1"

}



while IFS= read -r url

do

downloader "$url" &

done < urls.txt



wait

echo "Downloaded complete"



1. Скрипт читает построчно файл url.txt

2. Передает каждую строчку в функцию downloader

3. Функция downloader каждый раз запускается в фоне

4. Происходит скачивание файла по ссылке

5. По завершению (wait) получаем компливит complete



Файл url.txt представляет собой список прямых урлов на файлы, которые нужно скачать.



Получается мы не дожидаемся пока скачается первый файл, а сразу скачиваем все пачкой. Мультизагрузка.



Если совсем уж по-простому, то символ & говорит — запусти всё одновременно и параллельно. Типа такого, с таким ты уже всяко встречался:



hostname & date & uname &



Тут все запустится одновременно и порядок вывода на экран будет каждый раз в своем порядке. Все зависит с какой скоростью отработает команда.



Это основной вариант, советую его и использовать, теперь давай рассмотрим альтернативные.



Есть еще такая утилита parallel, устанавливается как и все остальное apt/yum/brew install parallel



Делает она то же самое, но имеет более гибкие настройки через параметры. Например, предыдущий пример со скачиванием файлов мог бы выглядеть таким образом:



parallel -j 4 wget -q {} < urls.txt



Ключ -j означает сколько джобов будет запараллелено, что-то типа потоков/threads.



Углубляться сейчас в parallel особого смысла не вижу, но для общего кругозора ты про нее должен знать. Иногда бывает что для решения задачи подойдет именно parallel, а не нативка с &.



Ну и на закуску
, есть такая штука «$!», она позволяет узнать PID последнего запущенного процесса. Давай на примере, запускай:



sleep 60 &

[1] 39032

echo $!

39032



В первой команде запустили фоном ожидание 60 секунд, вывелся порядковый номер джобы и PID. Ну а чтобы получить последний PID, выполняем третью команду с «$!». На экран вывелся PID в чистом виде, а дальше можешь его сохранить в переменную и уже проверять в скрипте, завершился он или нет.



Вот так это и работает. Ничего сложного. Надеюсь было интересно. Хорошего тебе понедельника, увидимся!



tags: #bash #linux #utils



💩 @bashdays