Respostas:
prog1 &
prog2 &
nohup
para impedir que o programa seja morto quando o shell desligar.
E se:
prog1 & prog2 && fg
Isso vai:
prog1
.prog2
e mantenha-o em primeiro plano , para que você possa fechá-lo ctrl-c
.prog2
, você vai voltar para prog1
's primeiro plano , então você também pode fechá-lo com ctrl-c
.prog1
quando prog2
termina? Pense em node srv.js & cucumberjs
prog1 & prog2 ; fg
Isso era para executar vários túneis ssh de uma só vez. Espero que isso ajude alguém.
prog2
não funcionar imediatamente, você voltará a ficar prog1
em primeiro plano. Se isso é desejável, está tudo bem.
prog1 & prog2 && kill $!
.
Você pode usar wait
:
some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
Ele atribui os PIDs do programa em segundo plano às variáveis ( $!
é o último PID do processo iniciado) e o wait
comando as espera. É legal porque se você matar o script, ele também matará os processos!
#!/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.."
${}
para interpolá-lo em uma lista de seqüências de caracteres ou similar.
Com o GNU Parallel http://www.gnu.org/software/parallel/ , é tão fácil quanto:
(echo prog1; echo prog2) | parallel
Ou se você preferir:
parallel ::: prog1 prog2
Saber mais:
parallel
diferentes com sintaxe diferente. Por exemplo, nos derivativos Debian, o moreutils
pacote contém um comando diferente chamado parallel
que se comporta de maneira bem diferente.
parallel
melhor do que usar &
?
parallel
é melhor quando há mais trabalhos que núcleos; nesse caso, &
seria executado mais de um trabalho por núcleo de uma só vez. (cf. princípio pigeonhole )
Se você deseja executar e matar com facilidade vários processos com facilidade ctrl-c
, este é o meu método favorito: gerar vários processos em segundo plano em um (…)
subshell e fazer uma armadilha SIGINT
para executar kill 0
, o que matará tudo o que for gerado no grupo de subshells:
(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
Você pode ter estruturas complexas de execução de processos, e tudo será fechado com uma única ctrl-c
(apenas verifique se o último processo é executado em primeiro plano, ou seja, não inclua um &
depois prog1.3
):
(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
xargs -P <n>
permite executar <n>
comandos em paralelo.
Embora -P
seja uma opção não padrão, as implementações GNU (Linux) e macOS / BSD a suportam.
O exemplo a seguir:
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
A saída se parece com:
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
O tempo mostra que os comandos foram executados em paralelo (o último comando foi iniciado somente após o término do primeiro dos 3 originais, mas executado muito rapidamente).
O xargs
comando em si não retornará até que todos os comandos tenham sido concluídos, mas você pode executá-lo em segundo plano encerrando-o com o operador de controle &
e, em seguida, usando o wait
builtin para aguardar xargs
a conclusão de todo o comando.
{
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 $!
Nota:
O BSD / macOS xargs
exige que você especifique a contagem de comandos a serem executados em paralelo explicitamente , enquanto o GNU xargs
permite especificar -P 0
a execução do maior número possível em paralelo.
A saída dos processos executados em paralelo chega à medida que está sendo gerada , portanto será imprevisivelmente intercalada .
parallel
, como mencionado na resposta de Ole (não é padrão na maioria das plataformas), serializa (agrupa) convenientemente a saída por processo e oferece muitos recursos mais avançados.#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
Redirecionar erros para separar logs.
prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log
. Os erros vão para o console e os dois arquivos de erro estão vazios. Como @Dennis Williamson diz, &
é um separador, assim ;
, (a) ele precisa ir no final do comando (após qualquer redirecionamento) e (b) você não precisa de ;
nada :-)
Existe um programa muito útil que chama nohup.
nohup - run a command immune to hangups, with output to a non-tty
nohup
por si só não executa nada em segundo plano, e o uso nohup
não é um requisito ou pré-requisito para executar tarefas em segundo plano. Eles geralmente são úteis juntos, mas, como tal, isso não responde à pergunta.
Aqui está uma função que eu uso para executar no máximo n processo em paralelo (n = 4 no exemplo):
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
Se max_children estiver definido como o número de núcleos, essa função tentará evitar núcleos inativos.
wait -n
requer bash
4.3+ e muda a lógica para aguardar o término de qualquer um dos processos especificados / implícitos.
Recentemente, tive uma situação semelhante em que precisava executar vários programas ao mesmo tempo, redirecionar suas saídas para arquivos de log separados e esperar que eles terminassem, e acabei com algo assim:
#!/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
Fonte: http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/
Gerente de Geração de Processos
Certamente, tecnicamente, esses são processos, e esse programa deve realmente ser chamado de gerenciador de criação de processos, mas isso se deve apenas à maneira como o BASH funciona quando bifurca usando o e comercial, usa a chamada de sistema fork () ou talvez clone () que clona em um espaço de memória separado, em vez de algo como pthread_create () que compartilharia memória. Se o BASH suportasse o último, cada "sequência de execução" funcionaria da mesma forma e poderia ser denominada como threads tradicionais, enquanto ganhava uma pegada de memória mais eficiente. No entanto, funcionalmente, funciona da mesma maneira, embora um pouco mais difícil, pois as variáveis GLOBAL não estão disponíveis em cada clone do trabalhador, portanto, o uso do arquivo de comunicação entre processos e o semáforo rudimentar do rebanho para gerenciar seções críticas. Bifurcação do BASH, é claro, é a resposta básica aqui, mas sinto que as pessoas sabem disso, mas realmente querem gerenciar o que é gerado, em vez de apenas bifurcar e esquecê-lo. Isso demonstra uma maneira de gerenciar até 200 instâncias de processos bifurcados, acessando um único recurso. Claramente, isso é um exagero, mas eu gostei de escrever, então continuei. Aumente o tamanho do seu terminal de acordo. Espero que você ache isso útil.
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
Seu script deve se parecer com:
prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.
Supondo que seu sistema possa executar n trabalhos por vez. use wait para executar apenas n trabalhos por vez.
Com o bashj ( https://sourceforge.net/projects/bashj/ ), você deve poder executar não apenas vários processos (da maneira que outros sugeriram), mas também vários Threads em uma JVM controlada a partir do seu script. Mas é claro que isso requer um JDK java. Threads consomem menos recursos que processos.
Aqui está um código de trabalho:
#!/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
! Sim, no bash você pode esperar pelos processos filhos do script.