Respuestas:
prog1 &
prog2 &
nohup
para evitar que el programa se elimine cuando el shell cuelga.
Qué tal si:
prog1 & prog2 && fg
Esta voluntad:
prog1
.prog2
y manténgalo en primer plano , para que pueda cerrarlo ctrl-c
.prog2
, volverá a prog1
's primer plano , por lo que puede también cerca con ctrl-c
.prog1
cuando prog2
termina? Piense en node srv.js & cucumberjs
prog1 & prog2 ; fg
esto fue para ejecutar múltiples túneles ssh a la vez. Espero que esto ayude a alguien.
prog2
no se ejecuta de inmediato, volverá a tener prog1
el primer plano. Si esto es deseable, entonces está bien.
prog1 & prog2 && kill $!
.
Puedes usar wait
:
some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
Asigna los PID del programa en segundo plano a las variables ( $!
es el último PID del proceso iniciado), luego el wait
comando los espera. Es bueno porque si matas el script, también mata los procesos.
#!/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 interpolarlo en una lista de cadenas o similar.
Con GNU Parallel http://www.gnu.org/software/parallel/ es tan fácil como:
(echo prog1; echo prog2) | parallel
O si lo prefieres:
parallel ::: prog1 prog2
Aprende más:
parallel
con diferentes sintaxis. Por ejemplo, en los derivados de Debian, el moreutils
paquete contiene un comando diferente llamado parallel
que se comporta de manera bastante diferente.
parallel
mejor que usar &
?
parallel
es mejor cuando hay más trabajos que núcleos, en cuyo caso &
ejecutaría más de un trabajo por núcleo a la vez. (cf. principio del casillero )
Si desea poder ejecutar y eliminar fácilmente múltiples procesos ctrl-c
, este es mi método favorito: generar múltiples procesos en segundo plano en una (…)
subshell y capturar SIGINT
para ejecutar kill 0
, lo que matará todo lo generado en el grupo de subshell:
(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
Puede tener estructuras de ejecución de proceso complejas, y todo se cerrará con una sola ctrl-c
(solo asegúrese de que el último proceso se ejecute en primer plano, es decir, no incluya un &
después prog1.3
):
(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
xargs -P <n>
le permite ejecutar <n>
comandos en paralelo.
Si bien -P
es una opción no estándar, tanto las implementaciones GNU (Linux) como macOS / BSD lo admiten.
El siguiente ejemplo:
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
El resultado se parece a:
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
El tiempo muestra que los comandos se ejecutaron en paralelo (el último comando se lanzó solo después de que finalizó el primero de los 3 originales, pero se ejecutó muy rápidamente).
El xargs
comando en sí no volverá hasta que todos los comandos hayan terminado, pero puede ejecutarlo en segundo plano al terminarlo con el operador de control &
y luego usar el wait
incorporado para esperar xargs
a que termine todo el 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:
BSD / macOS xargs
requiere que especifique el recuento de comandos para ejecutar en paralelo explícitamente , mientras que GNU le xargs
permite especificar -P 0
ejecutar tantos como sea posible en paralelo.
La salida de los procesos ejecutados en paralelo llega a medida que se genera , por lo que se intercalará de forma impredecible .
parallel
, como se menciona en la respuesta de Ole ( no viene estándar con la mayoría de las plataformas), serializa (agrupa) convenientemente la salida por proceso y ofrece muchas características más avanzadas.#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
Redirigir errores para separar los registros.
prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log
. Los errores van a la consola y ambos archivos de errores están vacíos. Como dice @Dennis Williamson, &
es un separador, ;
por lo que (a) debe ir al final del comando (después de cualquier redirección), y (b) no necesita ;
nada :-)
Hay un programa muy útil que llama nohup.
nohup - run a command immune to hangups, with output to a non-tty
nohup
por sí solo no ejecuta nada en segundo plano, y el uso nohup
no es un requisito o requisito previo para ejecutar tareas en segundo plano. A menudo son útiles juntos, pero como tal, esto no responde la pregunta.
Aquí hay una función que uso para ejecutar al máximo el proceso n en paralelo (n = 4 en el ejemplo):
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
Si max_children se establece en el número de núcleos, esta función intentará evitar los núcleos inactivos.
wait -n
requiere bash
4.3+ y cambia la lógica para esperar a que finalice cualquiera de los procesos especificados / implícitos.
Recientemente tuve una situación similar en la que necesitaba ejecutar varios programas al mismo tiempo, redirigir sus salidas a archivos de registro separados y esperar a que terminaran y terminé con algo así:
#!/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
Fuente: http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/
Gerente de proceso de desove
Claro, técnicamente estos son procesos, y este programa realmente debería llamarse un administrador de generación de procesos, pero esto solo se debe a la forma en que BASH funciona cuando se bifurca usando el ampersand, usa la llamada al sistema fork () o quizás clone () que clona en un espacio de memoria separado, en lugar de algo como pthread_create () que compartiría memoria. Si BASH admitiera lo último, cada "secuencia de ejecución" funcionaría de la misma manera y podría denominarse subprocesos tradicionales al tiempo que obtendría una huella de memoria más eficiente. Funcionalmente, sin embargo, funciona de la misma manera, aunque un poco más difícil ya que las variables GLOBALES no están disponibles en cada clon de trabajo, por lo tanto, el uso del archivo de comunicación entre procesos y el semáforo rudimentario de bandadas para gestionar secciones críticas. Bifurcar desde BASH, por supuesto, es la respuesta básica aquí, pero siento que la gente lo sabe, pero realmente está buscando administrar lo que se genera en lugar de simplemente bifurcarlo y olvidarlo. Esto demuestra una manera de administrar hasta 200 instancias de procesos bifurcados, todos accediendo a un solo recurso. Claramente, esto es excesivo, pero disfruté escribirlo, así que seguí. Aumente el tamaño de su terminal en consecuencia. Espero que encuentres esto ú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
Su guión debería verse así:
prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.
Asumiendo que su sistema puede tomar n trabajos a la vez. use esperar para ejecutar solo n trabajos a la vez.
Con bashj ( https://sourceforge.net/projects/bashj/ ), debería poder ejecutar no solo múltiples procesos (como lo sugirieron otros) sino también múltiples subprocesos en una JVM controlada desde su script. Pero, por supuesto, esto requiere un Java JDK. Los subprocesos consumen menos recursos que los procesos.
Aquí hay un código de trabajo:
#!/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
! Sí, en bash puedes esperar los procesos secundarios del script.