同时执行多个shell脚本

时间:2011-11-23 17:04:23

标签: bash shell parallel-processing

我想做以下事情:

  • 同时执行多个shell脚本(此处为2个脚本)。

  • 等到两个脚本完成

  • 转储每个脚本的返回值

但是,main.sh无效。


main.sh

#!/bin/bash

ret1=`./a.sh` &
ret2=`./b.sh`

if [ "${ret1}"="" -a "${ret2}"="" ]; then
   sleep 1
else
   echo ${ret1},${ret2}
end

a.sh

#!/bin/bash
sleep 10
echo 1

b.sh

#!/bin/bash
sleep 5
echo 2

5 个答案:

答案 0 :(得分:7)

如果你安装了GNU Parallel http://www.gnu.org/software/parallel/,你可以这样做:

parallel -j0 '{}; echo $?' ::: a.sh b.sh

我怀疑您是否希望退出代码检查其中一个是否失败,并且您实际上并不关心精确的退出代码是什么。在这种情况下,你可以这样做:

parallel -j0 ::: a.sh b.sh || echo one or both of them failed

如果足以获得失败的最后一个错误代码:

parallel -j0 --halt 1 ::: a.sh b.sh; echo $?

如果b.sh提前结束但是失败了,也许你想杀死a.sh:

parallel -j0 --halt 2 ::: a.sh b.sh; echo $?

您可以通过以下方式安装GNU Parallel:

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

观看GNU Parallel的介绍视频了解详情: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

答案 1 :(得分:5)

以下是我一直在运行的一些代码,它们似乎完全符合您的要求。只需在适当的位置插入./a.sh./b.sh

# Start the processes in parallel...
./script1.sh 1>/dev/null 2>&1 &
pid1=$!
./script2.sh 1>/dev/null 2>&1 &
pid2=$!
./script3.sh 1>/dev/null 2>&1 &
pid3=$!
./script4.sh 1>/dev/null 2>&1 &
pid4=$!

# Wait for processes to finish...
echo -ne "Commands sent... "
wait $pid1
err1=$?
wait $pid2
err2=$?
wait $pid3
err3=$?
wait $pid4
err4=$?

# Do something useful with the return codes...
if [ $err1 -eq 0 -a $err2 -eq 0 -a $err3 -eq 0 -a $err4 -eq 0 ]
then
    echo "pass"
else
    echo "fail"
fi

请注意,这会捕获脚本的退出状态,而不是它输出到stdout的内容。没有简单的方法来捕获在后台运行的脚本的stdout,因此我建议您使用exit status将信息返回给调用进程。

答案 2 :(得分:2)

您寻求的答案就是这个问题shell - get exit code of background process

基本上,当您处理后台进程时,无法直接获取其退出代码。但是如果你运行bash wait命令,那么wait的退出代码将返回后台进程的退出代码。

./a.sh &
pid1=$!
./b.sh
ret2=$?
wait ${pid1}
ret1=$?

即使a.sh在运行等待之前结束,这也会有效。特殊变量$?保存前一个过程的退出代码。 $!保存先前运行的进程的进程ID。

答案 3 :(得分:2)

如果您有bash 4.2或更高版本,则以下内容可能对您有用。它使用关联数组来存储任务名称及其“代码”以及任务名称及其pid。我还内置了一个简单的速率限制方法,如果你的任务消耗大量的CPU或I / O时间并且你想要限制并发任务的数量,那么它可能会派上用场。

脚本在第一个循环中启动所有任务,并在第二个循环中使用结果。

对于简单的情况,这有点过分,但它允许非常整洁的东西。例如,可以将每个任务的错误消息存储在另一个关联数组中,并在一切安定下来后打印它们。

(我已经从我的回答here复制了这个答案,因为它解决了这两个问题,如果不是这样,请告诉我或直接用链接或任何合适的替换它。)

#! /bin/bash

main () {
    local -A pids=()
    local -A tasks=([task1]="echo 1"
                    [task2]="echo 2"
                    [task3]="echo 3"
                    [task4]="false"
                    [task5]="echo 5"
                    [task6]="false")
    local max_concurrent_tasks=2

    for key in "${!tasks[@]}"; do
        while [ $(jobs 2>&1 | grep -c Running) -ge "$max_concurrent_tasks" ]; do
            sleep 1 # gnu sleep allows floating point here...
        done
        ${tasks[$key]} &
        pids+=(["$key"]="$!")
    done

    errors=0
    for key in "${!tasks[@]}"; do
        pid=${pids[$key]}
        local cur_ret=0
        if [ -z "$pid" ]; then
            echo "No Job ID known for the $key process" # should never happen
            cur_ret=1
        else
            wait $pid
            cur_ret=$?
        fi
        if [ "$cur_ret" -ne 0 ]; then
            errors=$(($errors + 1))
            echo "$key (${tasks[$key]}) failed."
        fi
    done

    return $errors
}

main

答案 4 :(得分:0)

反引号不会给出命令返回的值,而是命令的输出。要获得返回值:

#!/bin/sh

./a.sh &
./b.sh
ret2=$?   # get value returned by b.sh
wait %1   # Wait for a.sh to finish
ret1=$?   # get value returned by a.sh
echo "$ret1: $ret2"

如果你误解了这个问题并且确实想要输出命令,那么你也会得到它,因为它们都会转到脚本的标准输出。