Programing

bash 스크립트에서 여러 프로그램을 병렬로 어떻게 실행합니까?

lottogame 2020. 5. 1. 07:58
반응형

bash 스크립트에서 여러 프로그램을 병렬로 어떻게 실행합니까?


많은 프로그램을 동시에 실행 하는 .sh 파일 을 작성하려고합니다.

나는 이것을 시도했다

prog1 
prog2

그러나 그것은 prog1을 실행하고 prog1이 끝날 때까지 기다렸다가 prog2를 시작합니다 ...

어떻게 병렬로 실행할 수 있습니까?


prog1 &
prog2 &

어때요?

prog1 & prog2 && fg

이것은 :

  1. 시작하십시오 prog1.
  2. 백그라운드로 보내지 만 출력물을 계속 인쇄하십시오.
  3. 시작 prog2, 그리고 전경에 보관 당신이 그것을 닫을 수 있도록 ctrl-c.
  4. 때를 가까이 prog2, 당신에게 돌아갑니다 prog1'의 전경 도 가까이 함께 할 수있는 당신은 그래서 ctrl-c.

GNU Parallel http://www.gnu.org/software/parallel/을 사용하면 다음과 같이 쉽습니다.

(echo prog1; echo prog2) | parallel

또는 원하는 경우 :

parallel ::: prog1 prog2

더 알아보기:


당신은 사용할 수 있습니다 wait:

some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2

백그라운드 프로그램 PID를 변수 ( $!마지막으로 시작된 프로세스 PID)에 할당 한 다음 wait명령이 대기합니다. 스크립트를 죽이면 프로세스도 죽이기 때문에 좋습니다!


를 사용하여 여러 프로세스를 쉽게 실행하고 종료하려면 ctrl-c이것이 가장 좋아하는 방법입니다. 하위 셸에서 여러 백그라운드 프로세스를 생성 (…)하고 trap SIGINT을 실행 kill 0하면 하위 셸 그룹에 생성 된 모든 항목이 종료됩니다.

(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)

복잡한 프로세스 실행 구조를 가질 수 있으며 모든 것이 단일으로 닫힙니다 ctrl-c(마지막 프로세스가 포 그라운드에서 실행되도록하십시오 (즉, &after를 포함하지 마십시오 prog1.3)).

(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)

#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log

별도의 로그로 오류를 리디렉션하십시오.


nohup을 호출하는 매우 유용한 프로그램이 있습니다.

     nohup - run a command immune to hangups, with output to a non-tty

xargs -P <n><n>명령을 병렬 로 실행할 수 있습니다 .

하지만 -P비표준 옵션이며, 모두 GNU (리눅스) 및 맥 OS / BSD 구현을 지원합니다.

다음 예제는

  • 한 번 에 최대 3 개의 명령을 병렬로 실행
  • 이전에 시작된 프로세스가 종료 될 때만 추가 명령이 시작됩니다.
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

출력 결과는 다음과 같습니다.

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

타이밍은 명령이 병렬로 실행되었음을 나타냅니다 (마지막 명령은 원본 3 중 첫 번째가 종료 된 후에 만 ​​시작되었지만 매우 빠르게 실행 됨).

xargs모든 명령이 완료 될 때까지 명령 자체는 반환하지 않습니다,하지만 당신은 제어 연산자를 종료하여 백그라운드에서 그것을 실행할 수 &사용하여 다음과 wait전체 기다릴 내장을 xargs명령을 완료 할 수 있습니다.

{
  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 $!

노트 :

  • BSD / macOS xargs에서는 명시 적으로 병렬로 실행할 명령 xargs수를 지정해야 하지만 GNU 에서는 가능한 한 병렬 -P 0 실행 하도록 지정할 수 있습니다 .

  • 병렬로 실행되는 프로세스의 출력은 생성 될 때 도착 하므로 예기치 않게 인터리브 됩니다.

    • Ole의 답변 ( 대부분의 플랫폼에서 표준으로 제공 되지는 않음)parallel 에서 언급했듯이 GNU 프로세스별로 출력을 편리하게 직렬화 (그룹화)하고 더 많은 고급 기능을 제공합니다.

ppss 시도해 볼 수 있습니다 . ppss는 다소 강력합니다. 미니 클러스터를 만들 수도 있습니다. xargs -P는 당황스럽게 병렬 처리를 해야하는 경우 유용 할 수 있습니다.


max n 프로세스를 병렬로 실행하기 위해 사용하는 함수는 다음과 같습니다 (이 예에서는 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

max_children이 코어 수로 설정되면이 기능은 유휴 코어를 피하려고 시도합니다.


나는 최근에 동시에 여러 프로그램을 실행하고 출력을 분리 된 로그 파일로 리디렉션하고 완료 될 때까지 기다려야하는 비슷한 상황을 겪었습니다.

#!/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

출처 : http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/


프로세스 생성 관리자

물론 기술적으로는 프로세스이며,이 프로그램은 실제로 프로세스 스폰 관리자라고합니다. 그러나 이것은 앰퍼샌드를 사용하여 포크 할 때 BASH가 작동하는 방식 때문일뿐입니다. fork () 또는 아마도 clone () 시스템 호출을 사용합니다 메모리를 공유하는 pthread_create ()와 같은 것이 아니라 별도의 메모리 공간으로 복제됩니다. 만약 BASH가 후자를 지원한다면, 각각의 "시퀀스 실행"은 동일하게 작동 할 것이며보다 효율적인 메모리 풋 프린트를 얻는 동안 전통적인 스레드라고 할 수 있습니다. 그러나 GLOBAL 변수를 각 작업자 복제에서 사용할 수 없으므로 프로세스 간 통신 파일과 초보적 무리 세마포어를 사용하여 중요한 섹션을 관리하기 때문에 기능적으로 동일하게 작동합니다. 물론 BASH에서 포크하는 것이 기본적인 대답이지만 사람들은 그것을 알고있는 것처럼 느껴지지만 실제로 포크를하고 잊어 버리지 않고 생성 된 것을 관리하려고합니다. 이는 단일 리소스에 액세스하는 최대 200 개의 분기 프로세스 인스턴스를 관리하는 방법을 보여줍니다. 분명히 이것은 과잉입니다. 그러나 나는 그것을 계속 쓰는 것을 즐겼습니다. 이에 따라 터미널 크기를 늘리십시오. 이 정보가 도움이 되길 바랍니다.

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

With bashj ( https://sourceforge.net/projects/bashj/ ) , you should be able to run not only multiple processes (the way others suggested) but also multiple Threads in one JVM controlled from your script. But of course this requires a java JDK. Threads consume less resource than processes.

Here is a working code:

#!/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

참고URL : https://stackoverflow.com/questions/3004811/how-do-you-run-multiple-programs-in-parallel-from-a-bash-script

반응형