Запускайте паралельно кілька команд одночасно в одному терміналі


81

Я хочу запустити кілька команд, кожна з яких не виходить, доки не буде натиснуто Ctrl-C. Чи є щось, що я можу запустити, щоб запустити їх усіх відразу, і Ctrl-C вийде з них усіх? Вони можуть спільно використовувати вихід терміналу.

Зокрема, у мене є компілятор компаса, компілятор coffeescript та спеціальна команда, яка стежить за змінами файлів, усі запущені спостереження за змінами файлів. Я не хочу завантажувати термінал для кожної команди.

Відповіді:


123

Цей скрипт bash призначений для N паралельних потоків. Кожен аргумент - це команда.

trapбуде вбивати всі підпроцеси при лові SIGINT.
wait $PID_LISTчекає завершення кожного процесу. Коли всі процеси завершені, програма завершується.

#!/bin/bash

for cmd in "$@"; do {
  echo "Process \"$cmd\" started";
  $cmd & pid=$!
  PID_LIST+=" $pid";
} done

trap "kill $PID_LIST" SIGINT

echo "Parallel processes have started";

wait $PID_LIST

echo
echo "All processes have completed";

Збережіть цей сценарій як parallel_commandsі зробіть його виконуваним.
Ось як використовувати цей скрипт:

parallel_commands "cmd arg0 arg1 arg2" "other_cmd arg0 arg2 arg3"

Приклад:

parallel_commands "sleep 1" "sleep 2" "sleep 3" "sleep 4"

Почніть 4 паралельний сон і чекайте, поки "сон 4" закінчиться.


3
Дуже круто. Пастка - це те, що я шукав. Я відредагував вашу відповідь, включивши wait, що придушить непотрібні повідомлення "процес x вийшов".
Олівер Чжен

1
@MTsoul, waitкраще, ніж цикл while. Я покращив свій сценарій з вашою пропозицією. Зараз він також працює для N паралельних команд.
Алессандро Пеццато

А як щодо запуску кожного cmd на одному окремому ядрі? Чи слід запускати як: ./parallel_commands "taskset -c 0 cmd arg0 arg1 arg2" "taskset -c 1 other_cmd arg0 arg2 arg3" або taskset -c 2 ./parallel_commands "taskset -c 0 cmd arg0 arg1 arg2" "taskset -c 1 other_cmd arg0 arg2 arg3 "
користувач2517676

Настільки велика. Тепер у мене є кілька команд, які є в bash-сценарії, і я хочу запускати їх паралельно ?
shgnInc

1
Ви хочете STDOUT або значення виходу?
Алессандро Пеццато

90

На основі коментаря @ alessandro-pezzato. Запустіть кілька команд, використовуючи &між ними.

Приклад:

$ sleep 3 & sleep 5 & sleep 2 &

Він буде виконувати команди у фоновому режимі.


2
Це те, що я шукав.
TWR Cole

1
Привіт @OleTange, я не знаю різниці. Я намагався, &!і &обидва працювали однаково.
nsantana

Дякую за вашу пораду. Я міняю код і видаляю знак оклику.
nsantana

2
Control-C не повністю закінчує всі паралельні команди при такому виконанні, вони продовжуватимуть працювати у фоновому режимі. Який правильний спосіб закінчити виконання всіх команд?
Cal S

1
Запустіть fg, а потім натисніть Ctrl + C, повторіть за потреби
KTibow

35

Використовуйте GNU Parallel:

(echo command1; echo command2) | parallel
parallel ::: command1 command2

Вбивати:

parallel ::: command1 command2 &
PID=$!
kill -TERM $PID
kill -TERM $PID

1
@Oatman: Має бути, але паралельність GNU є не у всіх дистрибутивах. Наприклад, RHEL 7, здається, не має доступного пакета.
Стефан Маєвський

Чи можна відстежити PID паралельного процесу, який запускає кілька процесів, і завершити їх усі, завершуючи батьківський паралель?
WM

@StefanMajewsky: parallelзнаходиться у сховищі EPEL.
анонім

Попередження про цитати / шум насправді дратує! Це забруднює результати, ви можете замовкнути це так, але навіщо, на біса, це робити? Уявіть хаос, якби це зробили всі інструменти з відкритим кодом!
Pragmatic geek

@JavadSadeqzadeh Чи читали ви git.savannah.gnu.org/cgit/parallel.git/tree/doc/…
Ole Tange

3

Це можна зробити за допомогою простого Makefile:

sleep%:
        sleep $(subst sleep,,$@)
        @echo $@ done.

Використовуйте -jопцію.

$ make -j sleep3 sleep2 sleep1
sleep 3
sleep 2
sleep 1
sleep1 done.
sleep2 done.
sleep3 done.

Без -jопції він виконується послідовно.

$ make -j sleep3 sleep2 sleep1
sleep 3
sleep3 done.
sleep 2
sleep2 done.
sleep 1
sleep1 done.

Ви також можете зробити сухий запуск з опцією `-n '.

$ make -j -n sleep3 sleep2 sleep1
sleep 3
sleep 2
sleep 1

1

Я пропоную набагато простішу утиліту, про яку я щойно писав. Наразі він називається par, але незабаром буде перейменований на parl або pll, поки не вирішив.

https://github.com/k-bx/par

API такий простий, як:

par "script1.sh" "script2.sh" "script3.sh"

Команди префікса можна виконати за допомогою:

par "PARPREFIX=[script1] script1.sh" "script2.sh" "script3.sh"

1
Спробувавши та не виконавши описане вище за допомогою gnu паралельно, я з першої спроби вдався до використання вашої утиліти. Хороша робота!
worldsayshi

Перший: parallel ::: "script1.sh" "script2.sh" "script3.sh"Другий зазвичай використовував би --tag:parallel --tag ::: "script1.sh" "script2.sh" "script3.sh"
Оле

-13

Для запуску декількох команд просто додайте &&між двома командами, як це:command1 && command2

І якщо ви хочете запустити їх у двох різних терміналах, ви робите це так:

gnome-terminal -e "command1" && gnome-terminal -e "command2"

Це відкриє 2 термінали з command1і command2виконуючи в них.

Сподіваюся, це вам допоможе.


4
Це неправильно. Він запускається command2 після command1 і лише у випадку command1успіху. Спробуйте, sleep 3 && sleep 3і ви побачите, що це займе 6 с.
Марк Сетчелл

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