Antworten:
Wie wäre es mit:
prog1 & prog2 && fg
Dieser Wille:
prog1
.prog2
und halten Sie es im Vordergrund , damit Sie es mit schließen können ctrl-c
.prog2
, werden Sie zurückkommen prog1
‚s Vordergrund , so können Sie auch in der Nähe mit ihm ctrl-c
.prog1
wenn prog2
beendet wird? Denken Sie an node srv.js & cucumberjs
prog1 & prog2 ; fg
Dies diente zum gleichzeitigen Ausführen mehrerer SSH-Tunnel. Hoffe das hilft jemandem.
prog2
Sie wieder prog1
im Vordergrund stehen , wenn sie nicht sofort ausgeführt wird . Wenn dies wünschenswert ist, ist es in Ordnung.
prog1 & prog2 && kill $!
.
Sie können verwenden wait
:
some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
Es weist die Hintergrundprogramm-PIDs Variablen zu ( $!
ist die PID des zuletzt gestarteten Prozesses), und der wait
Befehl wartet auf sie. Es ist schön, denn wenn Sie das Skript beenden, werden auch die Prozesse beendet!
#!/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.."
${}
um es in eine Zeichenfolgenliste oder ähnliches zu interpolieren.
Mit GNU Parallel http://www.gnu.org/software/parallel/ ist es so einfach wie:
(echo prog1; echo prog2) | parallel
Oder wenn Sie es vorziehen:
parallel ::: prog1 prog2
Mehr erfahren:
parallel
mit unterschiedlicher Syntax gibt. Bei Debian-Derivaten moreutils
enthält das Paket beispielsweise einen anderen Befehl, parallel
der sich ganz anders verhält.
parallel
besser als mit &
?
parallel
ist besser, wenn mehr Jobs als Kerne vorhanden sind. In diesem Fall &
würden mehr als ein Job pro Kern gleichzeitig ausgeführt. (vgl. Pigeonhole-Prinzip )
Wenn Sie in der Lage sein möchten, mehrere Prozesse einfach auszuführen und zu beenden, ctrl-c
ist dies meine Lieblingsmethode: (…)
Mehrere Hintergrundprozesse in einer Subshell erzeugen und Trap SIGINT
ausführen kill 0
, wodurch alles getötet wird, was in der Subshell-Gruppe erzeugt wurde:
(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
Sie können komplexe Prozessausführung Strukturen haben, und alles wird in der Nähe mit einem einzigen ctrl-c
(nur sicherstellen , dass der letzte Prozess im Vordergrund ausgeführt wird, das heißt, sie sind nicht &
nach prog1.3
):
(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
xargs -P <n>
ermöglicht es Ihnen, <n>
Befehle parallel auszuführen.
Während -P
eine nicht standardmäßige Option ist, unterstützen sie sowohl die GNU- (Linux) als auch die macOS / BSD-Implementierung.
Das folgende Beispiel:
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
Die Ausgabe sieht ungefähr so aus:
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
Das Timing zeigt, dass die Befehle parallel ausgeführt wurden (der letzte Befehl wurde erst gestartet, nachdem der erste der ursprünglichen 3 beendet wurde, aber sehr schnell ausgeführt wurde).
Der xargs
Befehl selbst wird erst zurückgegeben, wenn alle Befehle abgeschlossen sind. Sie können ihn jedoch im Hintergrund ausführen, indem Sie ihn mit dem Steuerungsoperator beenden &
und dann mit dem integrierten wait
Befehl warten, bis der gesamte xargs
Befehl abgeschlossen ist.
{
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 $!
Hinweis:
Bei BSD / macOS xargs
müssen Sie die Anzahl der Befehle angeben, die explizit parallel ausgeführt werden sollen , während Sie bei GNU xargs
angeben können -P 0
, dass so viele Befehle wie möglich parallel ausgeführt werden sollen.
Die Ausgabe der parallel ausgeführten Prozesse kommt an, während sie generiert wird , sodass sie unvorhersehbar verschachtelt wird .
parallel
Wie in der Antwort von Ole erwähnt ( bei den meisten Plattformen nicht Standard), serialisiert (gruppiert) GNU die Ausgabe bequem pro Prozess und bietet viele erweiterte Funktionen.#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
Leiten Sie Fehler in separate Protokolle um.
prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log
. Die Fehler gehen an die Konsole und beide Fehlerdateien sind leer. Wie @Dennis Williamson sagt, &
ist ein Trennzeichen wie ;
: (a) es muss am Ende des Befehls stehen (nach jeder Umleitung), und (b) Sie brauchen das ;
überhaupt nicht :-)
Es gibt ein sehr nützliches Programm, das nohup aufruft.
nohup - run a command immune to hangups, with output to a non-tty
nohup
An sich wird nichts im Hintergrund ausgeführt, und die Verwendung nohup
ist keine Voraussetzung oder Voraussetzung für die Ausführung von Aufgaben im Hintergrund. Sie sind oft zusammen nützlich, aber als solche beantwortet dies die Frage nicht.
Hier ist eine Funktion, die ich verwende, um parallel mit maximal n Prozessen zu arbeiten (im Beispiel 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
Wenn max_children auf die Anzahl der Kerne eingestellt ist, versucht diese Funktion, Leerlaufkerne zu vermeiden.
wait -n
erfordert bash
4.3+ und ändert die Logik so, dass darauf gewartet wird, dass einer der angegebenen / implizierten Prozesse beendet wird.
Ich hatte kürzlich eine ähnliche Situation, in der ich mehrere Programme gleichzeitig ausführen, ihre Ausgaben in getrennte Protokolldateien umleiten und warten musste, bis sie fertig waren, und am Ende hatte ich Folgendes:
#!/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
Quelle: http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/
Prozess-Laich-Manager
Sicher, technisch gesehen sind dies Prozesse, und dieses Programm sollte eigentlich als Prozess-Spawning-Manager bezeichnet werden, aber dies liegt nur an der Funktionsweise von BASH, wenn es das kaufmännische Und verwendet, den Systemaufruf fork () oder vielleicht clone () verwendet Dies klont in einen separaten Speicherbereich und nicht in pthread_create (), das den Speicher gemeinsam nutzen würde. Wenn BASH Letzteres unterstützen würde, würde jede "Ausführungssequenz" genauso funktionieren und könnte als herkömmliche Threads bezeichnet werden, während ein effizienterer Speicherbedarf erzielt wird. Funktionell funktioniert es jedoch genauso, wenn auch etwas schwieriger, da nicht in jedem Worker-Klon GLOBAL-Variablen verfügbar sind, weshalb die prozessübergreifende Kommunikationsdatei und das rudimentäre Flock-Semaphor zur Verwaltung kritischer Abschnitte verwendet werden. Das Gabeln von BASH ist hier natürlich die grundlegende Antwort, aber ich habe das Gefühl, dass die Leute das wissen, aber wirklich versuchen, das zu verwalten, was erzeugt wird, anstatt es nur zu gabeln und zu vergessen. Dies zeigt eine Möglichkeit, bis zu 200 Instanzen von gegabelten Prozessen zu verwalten, die alle auf eine einzelne Ressource zugreifen. Natürlich ist das übertrieben, aber ich habe es genossen, es zu schreiben, also habe ich weitergemacht. Erhöhen Sie die Größe Ihres Terminals entsprechend. Ich hoffe, Sie finden das nützlich.
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
Ihr Skript sollte folgendermaßen aussehen:
prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.
Angenommen, Ihr System kann n Jobs gleichzeitig annehmen. Verwenden Sie wait, um jeweils nur n Jobs auszuführen.
Mit bashj ( https://sourceforge.net/projects/bashj/ ) sollten Sie nicht nur mehrere Prozesse ausführen können (wie von anderen vorgeschlagen), sondern auch mehrere Threads in einer JVM, die von Ihrem Skript aus gesteuert wird. Dies erfordert natürlich ein Java-JDK. Threads verbrauchen weniger Ressourcen als Prozesse.
Hier ist ein Arbeitscode:
#!/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
! Ja, in bash können Sie auf die untergeordneten Prozesse des Skripts warten.