我正在尝试编写一个同时运行许多程序的.sh文件

我尝试了此操作

prog1 
prog2


但是该程序运行prog1然后等待直到prog1结束然后启动prog2 ...

那么如何并行运行它们?

#1 楼

prog1 &
prog2 &


评论


不要忘记等待!是的,在bash中,您可以等待脚本的子进程。

– Dummy00001
2010-6-10 18:43



另一种选择是使用nohup防止外壳程序挂起时杀死程序。

– Philipp
10年7月24日在13:31

@liang:是的,它也可以与三个或更多程序一起使用。

–psmears
19年4月19日在22:01

#2 楼

怎么样:

prog1 & prog2 && fg


这将:


启动prog1
将其发送到后台,但继续打印
启动prog2,并将其保持在前台,因此可以使用ctrl-c将其关闭。
关闭prog2时,您将返回到prog1的前台,因此也可以使用ctrl-c


评论


当prog2终止时,是否有一种简单的方法来终止prog1?想想节点srv.js和Cucumingjs

– J P
2015年11月5日在16:31

只是尝试了一下,却没有达到我的预期。但是,稍作修改即可:prog1&prog2; fg这是用于一次运行多个ssh隧道。希望这对某人有帮助。

– jnadro52
16 Jan 20'在20:18



@ jnadro52,您的解决方案具有以下效果:如果prog2无法立即运行,您将回到前台prog1。如果这是理想的,那就没关系。

–油带
16年1月21日在9:29



在SSH的shell上,如果执行这样的命令,杀死prog1将非常棘手。 Ctrl-c对我不起作用。即使杀死整个终端,prog1仍在运行。

– Mercury0114
17年2月21日在20:44

@ jnadro52一种同时终止两个进程的方法是prog1&prog2 && kill $!。

– Zaboco
17年4月25日在16:35

#3 楼

您可以使用wait

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


它将后台程序PID分配给变量($!是最后启动的进程的PID),然后wait命令等待它们。很好,因为如果您终止脚本,它也会终止进程!

评论


以我的经验,杀死等待并不会杀死其他进程。

– Quinn Comendant
18年8月27日在5:47

如果我要循环启动后台进程,那么在继续执行下一组命令之前,如何等待每个后台进程完成。 #!/ usr / bin / env bash ARRAY ='cat bat rat'for $ ARRAY do中的ARR ./run_script1 $ ARR&done P1 = $!等待$ P1 echo“ INFO:for循环中所有后台进程的执行已完成。”

–睫毛
18-10-22在17:18



@Yash我认为您可以将进程ID保存到数组中,然后在该数组上调用wait。我认为您必须使用$ {}将其插值到字符串列表或类似列表中。

–trusktr
18年11月1日在21:23

最好的答案,对我来说,杀死脚本也会杀死进程! macOS Catalina,zsh控制台

– Michael Klishevich
5月6日下午16:07

使用等待无法杀死我的第二个进程。

– frodo2975
7小时前

#4 楼

如果要使用ctrl-c轻松运行并杀死多个进程,这是我最喜欢的方法:在(…)子外壳中生成多个后台进程,并捕获SIGINT来执行kill 0,这将杀死在子外壳组中生成的所有内容:

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


您可以具有复杂的流程执行结构,并且所有内容都将由一个ctrl-c关闭(只需确保最后一个流程在前台运行即可,即,不要包括在&之后的prog1.3):

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


评论


到目前为止,这是最好的答案。

– Nic
5月28日15:22

#5 楼

使用GNU Parallel http://www.gnu.org/software/parallel/可以轻松实现:

(echo prog1; echo prog2) | parallel


或者如果您愿意:

parallel ::: prog1 prog2


了解更多:


观看介绍视频以获取快速介绍:
https://www.youtube.com/ playlist?list = PL284C9FF2488BC6D1

遍历该教程(man parallel_tutorial)。您的命令行
会为您所爱。
阅读:Ole Tange,GNU Parallel 2018(Ole Tange,2018)。


评论


值得注意的是,存在具有不同语法的并行版本。例如,在Debian衍生产品上,moreutils软件包包含一个称为parallel的不同命令,其行为完全不同。

–乔尔·克罗斯(Joel Cross)
16年11月18日在14:24

比使用&更好?

–Optimus Prime
18/12/24在11:15

@OptimusPrime确实取决于。 GNU Parallel引入了一些开销,但作为回报,您可以对正在运行的作业和输出进行更多控制。如果同时打印两个作业,GNU Parallel将确保输出没有混合。

–奥雷·丹吉(Ole Tange)
18/12/25在2:44

如果作业多于核心,则@OptimusPrime并行会更好,在这种情况下,&每个核心一次运行多个作业。 (参见鸽洞原理)

–老年病
19年8月26日在18:09



@OleTange“您的命令行会为此而爱您。”我也是。 ☺

–老年病
19年8月30日在0:18

#6 楼

xargs -P <n>允许您并行运行<n>命令。
尽管-P是非标准选项,但GNU(Linux)和macOS / BSD实现均支持它。
以下示例:

一次最多可并行运行3个命令,
仅在先前启动的进程终止时才启动其他命令。

 time xargs -P 3 -I {} sh -c 'eval ""' - {} <<'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 ""' - {} <<'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要求您指定要显式并行运行的命令数,而GNU xargs允许您指定-P 0以尽可能多地并行运行。


并行运行的进程的输出会在生成时到达,因此将无法预料地交错。

Gle parallel,如Ole的回答所述(在大多数平台上不是标准的) ,可以方便地在每个进程的基础上对输出进行序列化(分组)并提供更多高级功能。




#7 楼

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


将错误重定向到单独的日志。

评论


您必须在重定向后放置“&”号,并省略分号(“&”号也将执行命令分隔符的功能):prog1 2> .errorprog1.log&prog2 2> .errorprog2.log&

–丹尼斯·威廉姆森
2010年6月9日10:42

分号执行两个命令,您可以测试de bash使其运行良好;)示例:pwd&2> .errorprog1.log;回显“ wop”和2> .errorprog2.log,当您将&放置在后台程序并立即执行下一条命令时。

– Fermin
2010年6月9日在22:49

它不起作用-错误不会重定向到文件。请尝试:ls notthere1&2> .errorprog1.log; ls notthere2&2> .errorprog2.log。错误进入控制台,并且两个错误文件均为空。就像@Dennis Williamson所说的,&是一个分隔符,如;,因此(a)它需要在命令的末尾(在任何redirecton之后),并且(b)您不需要;完全:-)

–psmears
2010-12-12 20:38

#8 楼

有一个非常有用的程序可以调用nohup。

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


评论


nohup本身不会在后台运行任何内容,使用nohup并不是在后台运行任务的必要条件或先决条件。它们经常一起使用,但是这样不能解决问题。

–tripleee
18年1月24日在15:32

#9 楼

这是我为了在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  as a description, you may be interested in other description
  echo "starting  ($time1)..."
  "$@" && time2=$(date +"%H:%M:%S") && echo "finishing  ($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设置为核心,此功能将尝试避免空闲的核心。

评论


不错的代码段,但是在我的bash中找不到“ wait -n”的解释,它说这是一个无效的选项。错字还是我错过了什么?

– Emmanuel Devaux
17 Mar 6 '17 at 14:10

@EmmanuelDevaux:wait -n需要bash 4.3+,它将逻辑更改为等待任何指定的/隐式进程终止。

–mklement0
17年5月22日在23:06

如果一项任务失败了,那我要结束脚本怎么办?

– 52coder
18年11月17日在3:19

@ 52coder可以调整功能以捕获失败的孩子,例如:“ $ @” && time2 = $(date +“%H:%M:%S”)&& echo“整理$ 2($ time1-$ time2 )...“ ||错误= 1&。然后在“ if”部分中测试错误,并在需要时中止该功能

– arnaldocan
18-11-18在17:31



#10 楼

您可以尝试ppss。 ppss相当强大-您甚至可以创建一个迷你集群。
xargs -P在您需要进行一系列令人尴尬的并行处理时也很有用。

#11 楼

最近,我遇到了类似的情况,需要同时运行多个程序,将它们的输出重定向到单独的日志文件,然后等待它们完成,然后我得到了类似的结果:

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

#12 楼

Process Spawning Manager

当然,从技术上讲,这些是进程,并且该程序实际上应被称为进程生成管理器,但这仅是由于BASH在使用&号进行分叉时使用的方式, fork()或clone()系统调用将克隆到单独的内存空间,而不是像pthread_create()这样的共享内存的东西。如果BASH支持后者,则每个“执行序列”将以相同的方式运行,并且可以被称为传统线程,同时获得更有效的内存占用。但是在功能上,它的工作原理相同,但由于在每个工作副本中都没有GLOBAL变量而更加困难,因此使用了进程间通信文件和基本的群信号量来管理关键部分,因此更加困难。从BASH分叉当然是这里的基本答案,但我觉得人们似乎知道这一点,但实际上是在设法管理产生的内容,而不是仅仅将其分叉而忘记了。这演示了一种管理多达200个分支进程实例的方法,这些实例都访问单个资源。显然,这太过分了,但是我喜欢写它,所以我坚持下去。相应地增加终端的大小。希望对您有用。

ME=$(basename q4312078q)
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[\;H

   if [ -n "" ];then
         if [[  = "good" ]];then
            echo -en "\033[1;32m"
         elif [[  = "warn" ]];then
            echo -en "\033[1;33m"
         elif [[  = "fail" ]];then
            echo -en "\033[1;31m"
         elif [[  = "crit" ]];then
            echo -en "\033[1;31;4m"
         fi
   fi
      echo -n ""
      echo -en "\033[0;39m"
}

trackthread()   #displays thread status
{
   WORKERID=
   THREADID=
   ACTION=    #setactive | setfree | update
   AGE=

   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=
   THREADID=
   #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 '3[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


#13 楼

您的脚本应类似于:

prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.


假设您的系统一次可以处理n个工作。使用wait一次只能运行n个作业。

#14 楼

如果您是:

在Mac上并拥有iTerm
要启动长期处于打开状态的各种进程,直到Ctrl + C
就可以轻松查看每个进程的输出
希望能够使用Ctrl + C轻松停止特定进程。

如果您的用例是更多的应用程序监视/管理,则一个选项是编写终端自身的脚本。
例如,我最近做了以下工作。授予它特定于Mac,特定于iTerm的功能,并依赖于不推荐使用的Apple Script API(iTerm具有更新的Python选项)。它没有获得任何优雅奖,但可以完成工作。
 #!/bin/sh
root_path="~/root-path"
auth_api_script="$root_path/auth-path/auth-script.sh"
admin_api_proj="$root_path/admin-path/admin.csproj"
agent_proj="$root_path/agent-path/agent.csproj"
dashboard_path="$root_path/dashboard-web"

osascript <<THEEND
tell application "iTerm"
  set newWindow to (create window with default profile)

  tell current session of newWindow
    set name to "Auth API"
    write text "pushd $root_path && $auth_api_script"
  end tell

  tell newWindow
    set newTab to (create tab with default profile)
    tell current session of newTab
        set name to "Admin API"
        write text "dotnet run --debug -p $admin_api_proj"
    end tell
  end tell

  tell newWindow
    set newTab to (create tab with default profile)
    tell current session of newTab
        set name to "Agent"
        write text "dotnet run --debug -p $agent_proj"
    end tell
  end tell

  tell newWindow
    set newTab to (create tab with default profile)
    tell current session of newTab
        set name to "Dashboard"
        write text "pushd $dashboard_path; ng serve -o"
    end tell
  end tell

end tell
THEEND
 



#15 楼

使用bashj(https://sourceforge.net/projects/bashj/),您不仅应该能够运行多个进程(其他人建议的方式),而且还可以在一个由脚本控制的JVM中运行多个线程。但这当然需要一个Java JDK。线程消耗的资源少于进程。

这是一个工作代码:

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


#16 楼

sh prog1;sh prog2

我认为这可行。.

评论


那将顺序地而不是并行地运行命令。

– Doj
12月9日10:45