Câu trả lời:
Làm thế nào về:
prog1 & prog2 && fg
Điều này sẽ:
prog1
.prog2
và giữ nó ở phía trước , để bạn có thể đóng nó với ctrl-c
.prog2
, bạn sẽ quay trở lại prog1
của tiền cảnh , vì vậy bạn cũng có thể đóng nó với ctrl-c
.prog1
khi prog2
chấm dứt? Hãy nghĩ về node srv.js & cucumberjs
prog1 & prog2 ; fg
Điều này là để chạy nhiều đường hầm ssh cùng một lúc. Hy vọng điều này sẽ giúp được ai đó.
prog2
không chạy ngay lập tức, bạn sẽ quay trở lại để có prog1
tiền cảnh. Nếu điều này là mong muốn, thì nó ổn.
prog1 & prog2 && kill $!
.
Bạn có thể sử dụng wait
:
some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
Nó gán các chương trình PID nền cho các biến ( $!
là quá trình khởi chạy cuối cùng 'PID), sau đó wait
lệnh chờ chúng. Thật tuyệt vì nếu bạn giết tập lệnh, nó cũng sẽ giết các tiến trình!
#!/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.."
${}
để nội suy nó thành một danh sách chuỗi hoặc tương tự.
Với GNU Parallel http://www.gnu.org/software/abul/ , thật dễ dàng như:
(echo prog1; echo prog2) | parallel
Hoặc nếu bạn thích:
parallel ::: prog1 prog2
Tìm hiểu thêm:
parallel
khác nhau với cú pháp khác nhau. Ví dụ, trên các dẫn xuất Debian, moreutils
gói chứa một lệnh khác được gọi là parallel
hành vi hoàn toàn khác.
parallel
tốt hơn so với sử dụng &
?
parallel
tốt hơn khi có nhiều công việc hơn lõi, trong trường hợp đó &
sẽ chạy nhiều công việc trên mỗi lõi cùng một lúc. (xem nguyên tắc pigeonhole )
Nếu bạn muốn có thể dễ dàng chạy và tiêu diệt nhiều tiến trình ctrl-c
, đây là phương pháp ưa thích của tôi: sinh ra nhiều quá trình nền trong một (…)
lớp con và bẫy SIGINT
để thực thi kill 0
, sẽ giết tất cả mọi thứ sinh ra trong nhóm lớp con:
(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
Bạn có thể có các cấu trúc thực thi quy trình phức tạp và mọi thứ sẽ đóng lại bằng một ctrl-c
(chỉ cần đảm bảo quy trình cuối cùng được chạy ở nền trước, nghĩa là không bao gồm một &
sau prog1.3
):
(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
xargs -P <n>
cho phép bạn chạy <n>
các lệnh song song.
Mặc dù -P
là một tùy chọn không chuẩn, cả hai triển khai GNU (Linux) và macOS / BSD đều hỗ trợ nó.
Ví dụ sau:
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
Đầu ra trông giống như:
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
Thời gian cho thấy các lệnh được chạy song song (lệnh cuối cùng được khởi chạy chỉ sau khi lệnh đầu tiên trong số 3 ban đầu bị chấm dứt, nhưng được thực thi rất nhanh).
Các xargs
lệnh riêng của mình sẽ không trở lại cho đến khi tất cả các lệnh đã kết thúc, nhưng bạn có thể thực hiện nó ở chế độ nền bằng cách chấm dứt nó với nhà điều hành kiểm soát &
và sau đó sử dụng wait
dựng sẵn để chờ đợi cho toàn bộ xargs
lệnh để kết thúc.
{
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 $!
Ghi chú:
BSD / macOS xargs
yêu cầu bạn chỉ định số lượng lệnh để chạy song song một cách rõ ràng , trong khi GNU xargs
cho phép bạn chỉ định -P 0
chạy song song càng nhiều càng tốt .
Đầu ra từ các quá trình chạy song song đến khi nó được tạo ra , do đó nó sẽ bị xen kẽ một cách khó lường .
parallel
, như đã đề cập trong câu trả lời của Ole ( không đạt tiêu chuẩn với hầu hết các nền tảng), thuận tiện tuần tự hóa (các nhóm) đầu ra trên cơ sở mỗi quy trình và cung cấp nhiều tính năng nâng cao hơn.#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
Chuyển hướng lỗi sang các bản ghi riêng biệt.
prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log
. Các lỗi đi đến bàn điều khiển và cả hai tệp lỗi đều trống. Như @Dennis Williamson nói, &
là một dấu phân cách, ;
vì vậy, (a) nó cần phải đi đến cuối lệnh (sau bất kỳ chuyển hướng nào) và (b) bạn không cần ;
gì cả :-)
Có một chương trình rất hữu ích gọi là nohup.
nohup - run a command immune to hangups, with output to a non-tty
nohup
tự nó không chạy bất cứ thứ gì trong nền và sử dụng nohup
không phải là một yêu cầu hoặc điều kiện tiên quyết để chạy các tác vụ trong nền. Chúng thường hữu ích với nhau nhưng như vậy, điều này không trả lời câu hỏi.
Đây là một hàm tôi sử dụng để chạy ở tiến trình max n song song (n = 4 trong ví dụ):
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
Nếu max_children được đặt thành số lõi, chức năng này sẽ cố gắng tránh các lõi nhàn rỗi.
wait -n
yêu cầu bash
4.3+ và nó thay đổi logic để chờ bất kỳ quy trình được chỉ định / ngụ ý nào chấm dứt.
Gần đây tôi đã gặp một tình huống tương tự khi tôi cần chạy nhiều chương trình cùng một lúc, chuyển hướng đầu ra của chúng sang các tệp nhật ký riêng biệt và đợi chúng kết thúc và tôi đã kết thúc với một cái gì đó như thế:
#!/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
Nguồn: http://joaoperibeiro.com/execute-multipl-programs-and-redirect-their-outputs-linux/
Quản lý sinh sản quá trình
Chắc chắn, về mặt kỹ thuật đây là các quy trình và chương trình này thực sự nên được gọi là trình quản lý sinh sản quy trình, nhưng điều này chỉ là do cách BASH hoạt động khi nó sử dụng ký hiệu, nó sử dụng lệnh gọi hệ thống fork () hoặc có thể là clone () mà nhân bản vào một không gian bộ nhớ riêng, thay vì một cái gì đó như pthread_create () sẽ chia sẻ bộ nhớ. Nếu BASH hỗ trợ cái sau, mỗi "chuỗi thực thi" sẽ hoạt động giống nhau và có thể được gọi là luồng truyền thống trong khi đạt được dấu chân bộ nhớ hiệu quả hơn. Tuy nhiên, về mặt chức năng, nó hoạt động như nhau, mặc dù khó khăn hơn một chút do các biến GLOBAL không có sẵn trong mỗi bản sao công nhân do đó sử dụng tệp truyền thông giữa các quá trình và semaphore thô sơ để quản lý các phần quan trọng. Ngã ba từ BASH tất nhiên là câu trả lời cơ bản ở đây nhưng tôi cảm thấy như mọi người biết điều đó nhưng thực sự đang tìm cách quản lý những gì được sinh ra thay vì chỉ rẽ nhánh và quên nó. Điều này cho thấy một cách để quản lý tới 200 trường hợp các quy trình rẽ nhánh tất cả truy cập vào một tài nguyên. Rõ ràng đây là quá mức cần thiết nhưng tôi thích viết nó vì vậy tôi tiếp tục. Tăng kích thước của thiết bị đầu cuối của bạn cho phù hợp. Tôi hy vọng bạn thấy nó hữu dụng.
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
Kịch bản của bạn sẽ giống như:
prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.
Giả sử hệ thống của bạn có thể nhận n công việc tại một thời điểm. sử dụng chờ để chỉ chạy n công việc tại một thời điểm.
Với bashj ( https://sourceforge.net/projects/bashj/ ), bạn sẽ có thể chạy không chỉ nhiều quy trình (theo cách mà người khác đề xuất) mà còn nhiều Chủ đề trong một JVM được kiểm soát từ tập lệnh của bạn. Nhưng tất nhiên điều này đòi hỏi một JDK java. Chủ đề tiêu thụ ít tài nguyên hơn các quy trình.
Đây là một mã làm việc:
#!/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
! Có, trong bash, bạn có thể đợi các tiến trình con của tập lệnh.