同时执行多个shell脚本

发布于 2024-12-17 19:25:00 字数 492 浏览 0 评论 0原文

我想做以下事情:

  • 同时执行多个 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

I want to do the following things:

  • Execute multiple shell scripts (here 2 scripts) concurrently.

  • Wait until both scripts finish

  • Dump return value of each script

However, main.sh does not work as expected.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

淡水深流 2024-12-24 19:25:00

如果您有 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 -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 883c667e01eed62f975ad28b6d50e22a
12345678 883c667e 01eed62f 975ad28b 6d50e22a
$ md5sum install.sh | grep cc21b4c943fd03e93ae1ae49e28573c0
cc21b4c9 43fd03e9 3ae1ae49 e28573c0
$ sha512sum install.sh | grep da012ec113b49a54e705f86d51e784ebced224fdf
79945d9d 250b42a4 2067bb00 99da012e c113b49a 54e705f8 6d51e784 ebced224
fdff3f52 ca588d64 e75f6033 61bd543f d631f592 2f87ceb2 ab034149 6df84a35
$ bash install.sh

观看GNU Parallel 的介绍视频以了解更多信息:
https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

打印作弊码工作表: https://www.gnu.org/software/parallel/parallel_cheat.pdf

If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:

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

I have a suspicion that you want the exit code to check if one of them failed, and that you actually do not care what the precise exit code was. In that case you can do:

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

If it is sufficient to get the error code of the last that failed:

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

Maybe you would like to kill a.sh if b.sh finishes early but fails:

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

You can install GNU Parallel simply by:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 883c667e01eed62f975ad28b6d50e22a
12345678 883c667e 01eed62f 975ad28b 6d50e22a
$ md5sum install.sh | grep cc21b4c943fd03e93ae1ae49e28573c0
cc21b4c9 43fd03e9 3ae1ae49 e28573c0
$ sha512sum install.sh | grep da012ec113b49a54e705f86d51e784ebced224fdf
79945d9d 250b42a4 2067bb00 99da012e c113b49a 54e705f8 6d51e784 ebced224
fdff3f52 ca588d64 e75f6033 61bd543f d631f592 2f87ceb2 ab034149 6df84a35
$ bash install.sh

Watch the intro videos for GNU Parallel to learn more:
https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Print the cheat sheet: https://www.gnu.org/software/parallel/parallel_cheat.pdf

菩提树下叶撕阳。 2024-12-24 19:25:00

这是我一直在运行的一些代码,它似乎完全符合您的要求。只需在适当的地方插入 ./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 ,因此我建议您使用 退出状态,将信息返回给调用进程。

Here is some code that I have been running, that seems to do exactly what you want. Just insert ./a.sh and ./b.sh where appropriate:

# 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

Note that this captures the exit status of the script and not what it outputs to stdout. There is no easy way of capturing the stdout of a script running in the background, so I would advise you to use the exit status to return information to the calling process.

触ぅ动初心 2024-12-24 19:25:00

您寻求的答案就在这个问题 shell - 获取后台进程的退出代码

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

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

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

The answer you seek is in this question shell - get exit code of background process

Basically, when you background a process you can't get its exit code directly. But if you run the bash wait command, then wait's exit code will return the exit code of the background process.

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

This will work even if a.sh ends before you run wait. The special variable $? holds the exit code of the previous process. And $! holds the Process ID of the previously run process.

陌上芳菲 2024-12-24 19:25:00

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

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

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

(我已从我的答案此处复制了这个答案,因为它解决了这两个问题,如果不行,请告诉我或替换直接通过链接或任何合适的方式即可。)

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

If you have bash 4.2 or later available the following might be useful to you. It uses associative arrays to store task names and their "code" as well as task names and their pids. I have also built in a simple rate-limiting method which might come handy if your tasks consume a lot of CPU or I/O time and you want to limit the number of concurrent tasks.

The script launches all tasks in the first loop and consumes the results in the second one.

This is a bit overkill for simple cases but it allows for pretty neat stuff. For example one can store error messages for each task in another associative array and print them after everything has settled down.

(I have copied this answer over from my answer here because it solves both questions, if that's not ok please tell me or replace it directly with just a link or whatever is suitable.)

#! /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
扬花落满肩 2024-12-24 19:25:00

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

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

如果您弄错了问题并且实际上想要命令的输出,您也会得到它,因为它们都会转到脚本的标准输出。

Backticks do not give the value returned by the command, but the output of the command. To get the return values:

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

If you mistated the question and do in fact want the output of the commands, you get that as well since they will both go to the stdout of the script.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文