Réponses:
Que diriez-vous:
prog1 & prog2 && fg
Cette volonté:
prog1
.prog2
et gardez-le au premier plan pour pouvoir le fermer ctrl-c
.prog2
, vous revenez à prog1
de » l'avant - plan , de sorte que vous pouvez refermer avec ctrl-c
.prog1
lorsque se prog2
termine? Pensez à node srv.js & cucumberjs
prog1 & prog2 ; fg
c'était pour exécuter plusieurs tunnels ssh à la fois. J'espère que cela aide quelqu'un.
prog2
ne s'exécute pas immédiatement, vous reviendrez prog1
au premier plan. Si cela est souhaitable, c'est ok.
prog1 & prog2 && kill $!
.
Vous pouvez utiliser wait
:
some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
Il attribue les PID du programme d'arrière-plan aux variables ( $!
est le dernier PID du processus lancé), puis la wait
commande les attend. C'est bien car si vous tuez le script, il tue aussi les processus!
#!/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.."
${}
pour l'interpoler dans une liste de chaînes ou similaire.
Avec GNU Parallel http://www.gnu.org/software/parallel/, c'est aussi simple que:
(echo prog1; echo prog2) | parallel
Ou si vous préférez:
parallel ::: prog1 prog2
Apprendre encore plus:
parallel
avec une syntaxe différente. Par exemple, sur les dérivés Debian, le moreutils
paquet contient une commande différente appelée parallel
qui se comporte assez différemment.
parallel
mieux que d'utiliser &
?
parallel
est meilleur lorsqu'il y a plus de travaux que de cœurs, auquel cas &
ils exécuteraient plus d'un travail par cœur à la fois. (cf. principe du pigeonhole )
Si vous voulez pouvoir exécuter et tuer facilement plusieurs processus avec ctrl-c
, voici ma méthode préférée: générer plusieurs processus d'arrière-plan dans un (…)
sous - shell et intercepter SIGINT
pour exécuter kill 0
, ce qui tuera tout ce qui est généré dans le groupe de sous-shell:
(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
Vous pouvez avoir des structures d'exécution de processus complexes, et tout se terminera par un seul ctrl-c
(assurez-vous simplement que le dernier processus est exécuté au premier plan, c'est-à-dire, n'incluez pas d' &
after prog1.3
):
(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
xargs -P <n>
vous permet d'exécuter des <n>
commandes en parallèle.
Bien qu'il -P
s'agisse d'une option non standard, les implémentations GNU (Linux) et macOS / BSD la prennent en charge.
L'exemple suivant:
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
La sortie ressemble à quelque chose comme:
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
Le timing montre que les commandes ont été exécutées en parallèle (la dernière commande n'a été lancée qu'après la fin du premier des 3 originaux, mais exécutée très rapidement).
La xargs
commande elle-même ne reviendra pas tant que toutes les commandes ne seront pas terminées, mais vous pouvez l'exécuter en arrière-plan en la terminant avec l'opérateur de contrôle &
, puis en utilisant la fonction wait
intégrée pour attendre la fin de la xargs
commande entière .
{
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 $!
Remarque:
BSD / macOS xargs
vous oblige à préciser le nombre de commandes à exécuter en parallèle explicitement , alors que GNU xargs
vous permet de spécifier -P 0
d'exécuter le plus grand nombre possible en parallèle.
La sortie des processus exécutés en parallèle arrive au fur et à mesure de sa génération , elle sera donc entrelacée de façon imprévisible .
parallel
, comme mentionné dans la réponse d'Ole ( non fourni en standard avec la plupart des plates-formes), sérialise (regroupe) commodément la sortie par processus et offre de nombreuses fonctionnalités plus avancées.#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
Redirigez les erreurs vers des journaux distincts.
prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log
. Les erreurs vont à la console et les deux fichiers d'erreur sont vides. Comme le dit @Dennis Williamson, &
est un séparateur, comme ;
, donc (a) il doit aller à la fin de la commande (après toute redirection), et (b) vous n'avez pas besoin du ;
tout :-)
Il existe un programme très utile qui appelle nohup.
nohup - run a command immune to hangups, with output to a non-tty
nohup
par lui-même n'exécute rien en arrière-plan, et l'utilisation nohup
n'est pas une exigence ou une condition préalable pour exécuter des tâches en arrière-plan. Ils sont souvent utiles ensemble mais en tant que tels, cela ne répond pas à la question.
Voici une fonction que j'utilise pour exécuter au max n processus en parallèle (n = 4 dans l'exemple):
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 est défini sur le nombre de cœurs, cette fonction essaiera d'éviter les cœurs inactifs.
wait -n
nécessite bash
4.3+ et il change la logique en attendant la fin de l' un des processus spécifiés / implicites.
J'ai eu une situation similaire récemment où j'avais besoin d'exécuter plusieurs programmes en même temps, de rediriger leurs sorties vers des fichiers journaux séparés et d'attendre qu'ils se terminent et je me suis retrouvé avec quelque chose comme ça:
#!/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
Source: http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/
Gestionnaire de génération de processus
Bien sûr, techniquement, ce sont des processus, et ce programme devrait vraiment être appelé un gestionnaire de génération de processus, mais cela n'est dû qu'à la façon dont BASH fonctionne quand il bifurque en utilisant l'esperluette, il utilise l'appel système fork () ou peut-être clone () qui clone dans un espace mémoire séparé, plutôt que quelque chose comme pthread_create () qui partagerait la mémoire. Si BASH supportait ce dernier, chaque "séquence d'exécution" fonctionnerait de la même manière et pourrait être qualifiée de threads traditionnels tout en gagnant une empreinte mémoire plus efficace. Fonctionnellement, cependant, cela fonctionne de la même manière, bien qu'un peu plus difficile car les variables GLOBALES ne sont pas disponibles dans chaque clone de travail, d'où l'utilisation du fichier de communication interprocessus et du sémaphore du troupeau rudimentaire pour gérer les sections critiques. Le fork de BASH est bien sûr la réponse de base ici, mais j'ai l'impression que les gens le savent mais cherchent vraiment à gérer ce qui est généré plutôt que de simplement le fork et l'oublier. Cela illustre un moyen de gérer jusqu'à 200 instances de processus bifurqués accédant tous à une seule ressource. De toute évidence, c'est exagéré, mais j'ai aimé l'écrire, alors j'ai continué. Augmentez la taille de votre terminal en conséquence. J'espère que vous trouvez ça utile.
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
Votre script devrait ressembler à:
prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.
En supposant que votre système peut prendre n tâches à la fois. utilisez wait pour exécuter uniquement n jobs à la fois.
Avec bashj ( https://sourceforge.net/projects/bashj/ ), vous devriez être en mesure d'exécuter non seulement plusieurs processus (comme d'autres l'ont suggéré) mais également plusieurs threads dans une machine virtuelle Java contrôlée à partir de votre script. Mais bien sûr, cela nécessite un JDK java. Les threads consomment moins de ressources que les processus.
Voici un code de travail:
#!/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
! Oui, dans bash, vous pouvez attendre les processus enfants du script.