В основном у вас есть три варианта:
export -f
(это функция, не относящаяся к POSIX bash ) Вариант 1, вероятно, проще всего продемонстрировать, поэтому я покажу его:
$ f() { num=$1; echo "$num is an integer"; }
$ export -f f
$ cat Commands.txt
number=1; f "$number"
number=2; f "$number"
number=3; f "$number"
number=4; f "$number"
number=5; f "$number"
$ parallel :::: Commands.txt
1 is an integer
2 is an integer
3 is an integer
4 is an integer
5 is an integer
Обратите внимание, что ваша популяция Commands.txt
, скорее всего, ошибочна, вам нужно f "$ number"
в Commands.txt
, чтобы передать номер, а не ] f число
, которое передало бы буквальную строку «число». Ваш скрипт для его генерации должен выполнять echo "number = $ i; f \" \ $ number \ ""
(обратите внимание на escape-символы, которые важны, чтобы избежать интерпретации $ number
в echo
время или "
с завершающей строки).
Соберите идентификаторы процессов, завершите фоновые процессы при выходе.
#!/bin/bash
killbg() {
for p in "${pids[@]}" ; do
kill "$p";
done
}
trap killbg EXIT
pids=()
background job 1 &
pids+=($!)
background job 2... &
pids+=($!)
foreground job
Перехват EXIT
запускает функцию при выходе из оболочки, независимо от причины. Вы можете изменить это на trap killbg SIGINT
, чтобы запускать его только на ^C
.
Это не проверяет, завершился ли один из фоновых процессов до того, как скрипт попытается выстрелить в них. Если они это сделают, вы можете получить ошибки или, что еще хуже, выстрелить не в тот процесс.
Или убить их по идентификатору работы. Давайте прочитаем вывод jobs
, чтобы выяснить, какие из них все еще активны.
#!/bin/bash
killjobs() {
for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do
kill %$x
done
}
trap killjobs EXIT
sleep 999 &
sleep 1 &
sleep 999 &
sleep 30
Если вы запускаете фоновые процессы, которые порождают другие процессы (например, подоболочку: (sleep 1234 ; echo foo) &
), вам необходимо включить управление заданиями с помощью set -m
( «режим монитора»), чтобы это работало. В противном случае завершается только ведущий процесс.
Если вы хотите завершить все фоновые процессы, если они не завершились до завершения выполнения lastProgram
, вам необходимо сохранить все pids:
python program1.py &
p1pid=$!
python program2.py &
p2pid=$!
other programs ... &
p3pid=$!
lastProgram
kill $p1pid $p2pid $p3pid
Если вы просто хотите дождаться завершения выполнения всех фоновых процессов перед выходом из скрипта, вы можете использовать команду wait
.
python program1.py &
python program2.py &
other programs ... &
lastProgram
wait
Я только что читал похожие вопросы о сборе PID и последующем уничтожении их всех в конце скрипта - проблема заключается в том, что PID для завершенного процесса может быть переработан и повторно использован в новом процессе до того, как ваш сценарий завершится, а затем вы можете убить новый (случайный) процесс.
Вы можете использовать управление заданиями bash, чтобы завершать только процессы, запущенные в сценарии, с помощью прерывания и задания %n, подсчитывая максимальное количество заданий, которые могут быть запущены (только 3 в этот пример):
#!/bin/bash
#trap 'kill %1 %2 %3' 0 # 0 or EXIT are equivalent
#trap 'kill %1 %2 %3' EXIT # or use {1..n} as below
trap 'kill %{1..3}' EXIT
sleep 33 &
sleep 33 &
sleep 33 &
echo processes are running, ctrl-c the next sleep
sleep 66
echo this line will never be executed
Любые дополнительные «убийства» несуществующих спецификаций заданий, которые уже завершились, приводят только к сообщению об ошибке, они не уничтожат никакие другие новые/случайные процессы.
Вот немного другой способ убить всю группу процессов вашего скрипта. Но если управление заданиями вашего скрипта/оболочки не настроено, то оно может наследовать PPID от своего родителя... но без управления заданиями описанное выше тоже не сработает.
Разница в том, что эта команда kill для trap использует PID bash, поскольку он становится PGID для новых процессов:
trap 'kill -- -$$' EXIT
См. этот связанный Q или здесь, где Йоханнес 'fish' Ziemke ловит SIGINT и SIGTERM и использует setsid
для «уничтожения всей группы процессов в новой группе процессов, чтобы мы не рисковали убить самих себя.»)
Вот моя однострочная команда (вbash
):
trap "jobs -p | xargs kill ; trap - INT" INT ; cmd1 & cmd2 & cmd3 &
# replace `cmds` with your command
Он перехватывает Ctrl + C , чтобы закрыть все фоновые задания и вернуться к поведению по умолчанию.
Затем все команды выполняются в фоновом режиме.
При нажатии Ctrl + C ,он убьет их всех и вернет ловушку к ее поведению по умолчанию.