Ответы:
Как насчет:
prog1 & prog2 && fg
Это будет:
prog1
.prog2
и держите его на переднем плане , чтобы вы могли закрыть его ctrl-c
.prog2
, вы будете возвращаться к prog1
«S переднего плана , так что вы также можете закрыть его с ctrl-c
.prog1
когда prog2
заканчивается? Подумайте о node srv.js & cucumberjs
prog1 & prog2 ; fg
это было для запуска нескольких туннелей SSH одновременно. Надеюсь, это кому-нибудь поможет.
prog2
не запустится, вы вернетесь к тому, что prog1
на переднем плане. Если это желательно, тогда все в порядке.
prog1 & prog2 && kill $!
.
Вы можете использовать wait
:
some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
Он назначает переменные PID фоновой программы (это PID $!
последнего запущенного процесса), затем wait
команда ожидает их. Это хорошо, потому что если вы убьете скрипт, он тоже убьет процессы!
#!/usr/bin/env bash ARRAY='cat bat rat' for ARR in $ARRAY do ./run_script1 $ARR & done P1=$! wait $P1 echo "INFO: Execution of all background processes in the for loop has completed.."
${}
чтобы интерполировать его в список строк или тому подобное.
С GNU Parallel http://www.gnu.org/software/parallel/ это так же просто, как:
(echo prog1; echo prog2) | parallel
Или, если вы предпочитаете:
parallel ::: prog1 prog2
Выучить больше:
parallel
с разным синтаксисом. Например, в производных Debian moreutils
пакет содержит другую команду с именем, parallel
которая ведет себя совершенно иначе.
parallel
лучше , чем использовать &
?
parallel
лучше, когда имеется больше заданий, чем ядер, и в этом случае &
на одно ядро можно запускать более одного задания одновременно. (см принцип закуток )
Если вы хотите иметь возможность легко запускать и уничтожать несколько процессов ctrl-c
, это мой любимый метод: порождать несколько фоновых процессов в (…)
подоболочке и ловушку SIGINT
для выполнения kill 0
, которая уничтожит все, что появилось в группе подоболочек:
(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
У вас могут быть сложные структуры выполнения процесса, и все будет закрываться одним ctrl-c
(просто убедитесь, что последний процесс запущен на переднем плане, т. Е. Не включайте &
после prog1.3
):
(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
xargs -P <n>
позволяет выполнять <n>
команды параллельно.
Хотя -P
это нестандартный вариант, реализации GNU (Linux) и macOS / BSD поддерживают его.
Следующий пример:
time xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF
Вывод выглядит примерно так:
1 # output from 1st command
4 # output from *last* command, which started as soon as the count dropped below 3
2 # output from 2nd command
3 # output from 3rd command
real 0m3.012s
user 0m0.011s
sys 0m0.008s
Время показывает, что команды были запущены параллельно (последняя команда была запущена только после того, как первая из трех оригинальных команд завершилась, но была выполнена очень быстро).
Сама xargs
команда не вернется, пока все команды не будут завершены, но вы можете выполнить ее в фоновом режиме, завершив ее оператором управления, &
а затем используя wait
встроенную функцию для ожидания завершения всей xargs
команды.
{
xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF
} &
# Script execution continues here while `xargs` is running
# in the background.
echo "Waiting for commands to finish..."
# Wait for `xargs` to finish, via special variable $!, which contains
# the PID of the most recently started background process.
wait $!
Примечание:
BSD / macOS xargs
требует, чтобы вы указали количество команд для параллельного запуска в явном виде , тогда как GNU xargs
позволяет вам задавать параллельное -P 0
выполнение максимально возможного числа команд .
Выходные данные параллельных процессов поступают по мере их генерации , поэтому они будут непредсказуемо чередоваться .
parallel
, как упомянуто в ответе Оле ( не входит в стандартную комплектацию большинства платформ), удобно сериализует (группирует) вывод для каждого процесса и предлагает множество дополнительных функций.#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
Перенаправлять ошибки в отдельные журналы.
prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log
. Ошибки отправляются на консоль, и оба файла ошибок пусты. Как говорит @Dennis Williamson, &
это разделитель, например ;
, (а) он должен идти в конце команды (после любого перенаправления), и (б) вам вообще не нужен ;
:-)
Есть очень полезная программа, которая вызывает nohup.
nohup - run a command immune to hangups, with output to a non-tty
nohup
сам по себе ничего не запускает в фоновом режиме, и использование nohup
не является обязательным или обязательным условием для запуска задач в фоновом режиме. Они часто полезны вместе, но как таковой, это не отвечает на вопрос.
Вот функция, которую я использую для параллельного запуска процесса max n (в примере n = 4):
max_children=4
function parallel {
local time1=$(date +"%H:%M:%S")
local time2=""
# for the sake of the example, I'm using $2 as a description, you may be interested in other description
echo "starting $2 ($time1)..."
"$@" && time2=$(date +"%H:%M:%S") && echo "finishing $2 ($time1 -- $time2)..." &
local my_pid=$$
local children=$(ps -eo ppid | grep -w $my_pid | wc -w)
children=$((children-1))
if [[ $children -ge $max_children ]]; then
wait -n
fi
}
parallel sleep 5
parallel sleep 6
parallel sleep 7
parallel sleep 8
parallel sleep 9
wait
Если для max_children задано количество ядер, эта функция будет пытаться избежать незанятых ядер.
wait -n
требуется bash
4.3+, и он меняет логику на ожидание завершения любого из указанных / подразумеваемых процессов.
Недавно у меня была похожая ситуация, когда мне нужно было запускать несколько программ одновременно, перенаправлять их выходные данные в отдельные файлы журнала и ждать, пока они закончат, и в итоге я получил нечто подобное:
#!/bin/bash
# Add the full path processes to run to the array
PROCESSES_TO_RUN=("/home/joao/Code/test/prog_1/prog1" \
"/home/joao/Code/test/prog_2/prog2")
# You can keep adding processes to the array...
for i in ${PROCESSES_TO_RUN[@]}; do
${i%/*}/./${i##*/} > ${i}.log 2>&1 &
# ${i%/*} -> Get folder name until the /
# ${i##*/} -> Get the filename after the /
done
# Wait for the processes to finish
wait
Источник: http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/
Менеджер процесса порождения
Конечно, технически это процессы, и эта программа должна называться диспетчером порождения процессов, но это только из-за того, что BASH работает, когда он разветвляется с помощью амперсанда, он использует системный вызов fork () или, возможно, clone () который клонируется в отдельное пространство памяти, а не что-то вроде pthread_create (), который бы разделял память. Если бы BASH поддерживал последнее, каждая «последовательность выполнения» работала бы одинаково и могла бы быть названа традиционными потоками, получая при этом более эффективный объем памяти. Функционально, однако, он работает так же, хотя и немного сложнее, поскольку переменные GLOBAL недоступны в каждом рабочем клоне, поэтому для управления критическими секциями используется файл межпроцессного взаимодействия и элементарный семафор стада. Формирование от BASH, конечно, является основным ответом здесь, но я чувствую, что люди знают это, но действительно хотят управлять тем, что порождено, а не просто раскошелиться и забыть это. Это демонстрирует способ управления до 200 экземплярами разветвленных процессов, каждый из которых обращается к одному ресурсу. Ясно, что это излишне, но мне понравилось писать, поэтому я продолжал. Увеличьте размер вашего терминала соответственно. Я надеюсь, что вы найдете это полезным.
ME=$(basename $0)
IPC="/tmp/$ME.ipc" #interprocess communication file (global thread accounting stats)
DBG=/tmp/$ME.log
echo 0 > $IPC #initalize counter
F1=thread
SPAWNED=0
COMPLETE=0
SPAWN=1000 #number of jobs to process
SPEEDFACTOR=1 #dynamically compensates for execution time
THREADLIMIT=50 #maximum concurrent threads
TPS=1 #threads per second delay
THREADCOUNT=0 #number of running threads
SCALE="scale=5" #controls bc's precision
START=$(date +%s) #whence we began
MAXTHREADDUR=6 #maximum thread life span - demo mode
LOWER=$[$THREADLIMIT*100*90/10000] #90% worker utilization threshold
UPPER=$[$THREADLIMIT*100*95/10000] #95% worker utilization threshold
DELTA=10 #initial percent speed change
threadspeed() #dynamically adjust spawn rate based on worker utilization
{
#vaguely assumes thread execution average will be consistent
THREADCOUNT=$(threadcount)
if [ $THREADCOUNT -ge $LOWER ] && [ $THREADCOUNT -le $UPPER ] ;then
echo SPEED HOLD >> $DBG
return
elif [ $THREADCOUNT -lt $LOWER ] ;then
#if maxthread is free speed up
SPEEDFACTOR=$(echo "$SCALE;$SPEEDFACTOR*(1-($DELTA/100))"|bc)
echo SPEED UP $DELTA%>> $DBG
elif [ $THREADCOUNT -gt $UPPER ];then
#if maxthread is active then slow down
SPEEDFACTOR=$(echo "$SCALE;$SPEEDFACTOR*(1+($DELTA/100))"|bc)
DELTA=1 #begin fine grain control
echo SLOW DOWN $DELTA%>> $DBG
fi
echo SPEEDFACTOR $SPEEDFACTOR >> $DBG
#average thread duration (total elapsed time / number of threads completed)
#if threads completed is zero (less than 100), default to maxdelay/2 maxthreads
COMPLETE=$(cat $IPC)
if [ -z $COMPLETE ];then
echo BAD IPC READ ============================================== >> $DBG
return
fi
#echo Threads COMPLETE $COMPLETE >> $DBG
if [ $COMPLETE -lt 100 ];then
AVGTHREAD=$(echo "$SCALE;$MAXTHREADDUR/2"|bc)
else
ELAPSED=$[$(date +%s)-$START]
#echo Elapsed Time $ELAPSED >> $DBG
AVGTHREAD=$(echo "$SCALE;$ELAPSED/$COMPLETE*$THREADLIMIT"|bc)
fi
echo AVGTHREAD Duration is $AVGTHREAD >> $DBG
#calculate timing to achieve spawning each workers fast enough
# to utilize threadlimit - average time it takes to complete one thread / max number of threads
TPS=$(echo "$SCALE;($AVGTHREAD/$THREADLIMIT)*$SPEEDFACTOR"|bc)
#TPS=$(echo "$SCALE;$AVGTHREAD/$THREADLIMIT"|bc) # maintains pretty good
#echo TPS $TPS >> $DBG
}
function plot()
{
echo -en \\033[${2}\;${1}H
if [ -n "$3" ];then
if [[ $4 = "good" ]];then
echo -en "\\033[1;32m"
elif [[ $4 = "warn" ]];then
echo -en "\\033[1;33m"
elif [[ $4 = "fail" ]];then
echo -en "\\033[1;31m"
elif [[ $4 = "crit" ]];then
echo -en "\\033[1;31;4m"
fi
fi
echo -n "$3"
echo -en "\\033[0;39m"
}
trackthread() #displays thread status
{
WORKERID=$1
THREADID=$2
ACTION=$3 #setactive | setfree | update
AGE=$4
TS=$(date +%s)
COL=$[(($WORKERID-1)/50)*40]
ROW=$[(($WORKERID-1)%50)+1]
case $ACTION in
"setactive" )
touch /tmp/$ME.$F1$WORKERID #redundant - see main loop
#echo created file $ME.$F1$WORKERID >> $DBG
plot $COL $ROW "Worker$WORKERID: ACTIVE-TID:$THREADID INIT " good
;;
"update" )
plot $COL $ROW "Worker$WORKERID: ACTIVE-TID:$THREADID AGE:$AGE" warn
;;
"setfree" )
plot $COL $ROW "Worker$WORKERID: FREE " fail
rm /tmp/$ME.$F1$WORKERID
;;
* )
;;
esac
}
getfreeworkerid()
{
for i in $(seq 1 $[$THREADLIMIT+1])
do
if [ ! -e /tmp/$ME.$F1$i ];then
#echo "getfreeworkerid returned $i" >> $DBG
break
fi
done
if [ $i -eq $[$THREADLIMIT+1] ];then
#echo "no free threads" >> $DBG
echo 0
#exit
else
echo $i
fi
}
updateIPC()
{
COMPLETE=$(cat $IPC) #read IPC
COMPLETE=$[$COMPLETE+1] #increment IPC
echo $COMPLETE > $IPC #write back to IPC
}
worker()
{
WORKERID=$1
THREADID=$2
#echo "new worker WORKERID:$WORKERID THREADID:$THREADID" >> $DBG
#accessing common terminal requires critical blocking section
(flock -x -w 10 201
trackthread $WORKERID $THREADID setactive
)201>/tmp/$ME.lock
let "RND = $RANDOM % $MAXTHREADDUR +1"
for s in $(seq 1 $RND) #simulate random lifespan
do
sleep 1;
(flock -x -w 10 201
trackthread $WORKERID $THREADID update $s
)201>/tmp/$ME.lock
done
(flock -x -w 10 201
trackthread $WORKERID $THREADID setfree
)201>/tmp/$ME.lock
(flock -x -w 10 201
updateIPC
)201>/tmp/$ME.lock
}
threadcount()
{
TC=$(ls /tmp/$ME.$F1* 2> /dev/null | wc -l)
#echo threadcount is $TC >> $DBG
THREADCOUNT=$TC
echo $TC
}
status()
{
#summary status line
COMPLETE=$(cat $IPC)
plot 1 $[$THREADLIMIT+2] "WORKERS $(threadcount)/$THREADLIMIT SPAWNED $SPAWNED/$SPAWN COMPLETE $COMPLETE/$SPAWN SF=$SPEEDFACTOR TIMING=$TPS"
echo -en '\033[K' #clear to end of line
}
function main()
{
while [ $SPAWNED -lt $SPAWN ]
do
while [ $(threadcount) -lt $THREADLIMIT ] && [ $SPAWNED -lt $SPAWN ]
do
WID=$(getfreeworkerid)
worker $WID $SPAWNED &
touch /tmp/$ME.$F1$WID #if this loops faster than file creation in the worker thread it steps on itself, thread tracking is best in main loop
SPAWNED=$[$SPAWNED+1]
(flock -x -w 10 201
status
)201>/tmp/$ME.lock
sleep $TPS
if ((! $[$SPAWNED%100]));then
#rethink thread timing every 100 threads
threadspeed
fi
done
sleep $TPS
done
while [ "$(threadcount)" -gt 0 ]
do
(flock -x -w 10 201
status
)201>/tmp/$ME.lock
sleep 1;
done
status
}
clear
threadspeed
main
wait
status
echo
Ваш скрипт должен выглядеть так:
prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.
Предполагая, что ваша система может выполнять n заданий одновременно. используйте wait для запуска только n заданий одновременно.
С помощью bashj ( https://sourceforge.net/projects/bashj/ ) вы сможете запускать не только несколько процессов (как предлагали другие), но и несколько потоков в одной JVM, управляемой из вашего сценария. Но, конечно, это требует Java JDK. Потоки потребляют меньше ресурсов, чем процессы.
Вот рабочий код:
#!/usr/bin/bashj
#!java
public static int cnt=0;
private static void loop() {u.p("java says cnt= "+(cnt++));u.sleep(1.0);}
public static void startThread()
{(new Thread(() -> {while (true) {loop();}})).start();}
#!bashj
j.startThread()
while [ j.cnt -lt 4 ]
do
echo "bash views cnt=" j.cnt
sleep 0.5
done
wait
! Да, в bash вы можете дождаться дочерних процессов скрипта.