如何 运行 bash 脚本并行而不是顺序?

How to run the bash script in parallel rather than sequentially?

#!/bin/bash

some_array=()

echo "-- Setting-Up VM --"

for i in ${some_array[@]}; do
    echo "VM #: $i"

    case "$i" in

        "1")
        echo "Setting-Up VM $i"
        sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@10.xx.x.xx <<EOF
        pwd
        nohup yes | /etc/rc.d/init.d/lifconfig
        su tarts
        nohup yes | vncserver
        sleep 10
        exit
        exit
EOF
        ;;

        "2")
        echo "Setting-Up VM $i"
        sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@10.xx.x.xx <<EOF
        pwd
        nohup yes | /etc/rc.d/init.d/lifconfig
        su tarts
        nohup yes | vncserver
        sleep 10
        exit
        exit
EOF
        ;;

        "3")
        echo "Setting-Up VM $i"
        sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@10.xx.x.xx <<EOF
        pwd
        nohup yes | /etc/rc.d/init.d/lifconfig
        su tarts
        nohup yes | vncserver
        sleep 10
        exit
        exit
EOF
        ;;
        *)
        echo "unknown VM!"
        ;;
    esac
done

有人可以指导我吗?我有上面的脚本,当我们 运行 脚本 ./vmSetup.sh "1 2 3" 时执行,但这是按顺序执行的。我已经创建了这个脚本,但现在我想 运行 脚本中的案例,即并行的 1、2 和 3。有人还可以告诉我如何 运行 例如并行处理 8 个案例吗?

将每个 case ... esac 语句以 & 结尾放在后台。然后在循环后使用wait等待所有后台进程完成。

#!/bin/bash

some_array=()

echo "-- Setting-Up VM --"

for i in ${some_array[@]}; do
    echo "VM #: $i"

    case "$i" in

        "1")
        echo "Setting-Up VM $i"
        sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@10.xx.x.xx <<EOF
        pwd
        nohup yes | /etc/rc.d/init.d/lifconfig
        su tarts
        nohup yes | vncserver
        sleep 10
        exit
        exit
EOF
        ;;

        "2")
        echo "Setting-Up VM $i"
        sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@10.xx.x.xx <<EOF
        pwd
        nohup yes | /etc/rc.d/init.d/lifconfig
        su tarts
        nohup yes | vncserver
        sleep 10
        exit
        exit
EOF
        ;;

        "3")
        echo "Setting-Up VM $i"
        sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@10.xx.x.xx <<EOF
        pwd
        nohup yes | /etc/rc.d/init.d/lifconfig
        su tarts
        nohup yes | vncserver
        sleep 10
        exit
        exit
EOF
        ;;
        *)
        echo "unknown VM!"
        ;;
    esac &
done

wait

当所有这些都相同时,为什么还要有 case 语句?

#!/bin/bash
echo "-- Setting-Up VM(s) --"
for i in "$@"; do
case "$i" in
1) IP=1.2.3.4;;
2) IP=2.2.3.4;;
3) IP=3.2.3.4;;
*) echo "Invalid option '$i'" >&2; exit 1;;
esac
echo "Setting-Up VM $i"
sshpass -p root12 ssh -tt -o StrictHostKeyChecking=no root@$IP <<EOF &
  pwd
  nohup yes | /etc/rc.d/init.d/lifconfig
  su tarts
     nohup yes | vncserver
     sleep 10
     exit
  exit
EOF
done

要运行后台作业,只需使用&

sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@$IP <<EOF &

它只是因为这里的文档而令人困惑,但 & 元字符在我的系统上仍然可以正确解析。试试吧。

此外,与其在一个字符串中引用所有选项然后将它们解析回数组,为什么不简化整个事情并让它们作为单独的参数出现?称之为

./vmSetup.sh 1 2 3 # NOT "1 2 3" with quotes

循环就变成了

for i in "$@" # properly quoted, though wouldn't matter for 1 2 3

整个事情看起来更简单,更容易维护。

我会做的另一种选择:创建一个包含您需要的 DNS/IP 地址列表的文件,然后将该文件作为单独的参数传递。

#!/bin/bash
while read -r addr || [[ -n "$addr" ]]
do echo "Setting-Up VM $addr"
sshpass -p "root12"  ssh -tt -o StrictHostKeyChecking=no root@$addr <<EOF &
  pwd
  nohup yes | /etc/rc.d/init.d/lifconfig
  su tarts
     nohup yes | vncserver
     sleep 10
     exit
  exit
EOF
done < ""

最好先添加有效的错误检查以确保文件存在且可读等,但作为一个简单的案例,这应该可行。

要获得更好、更干净、更安全的代码,请阅读 https://mywiki.wooledge.org/BashFAQ/001 并遵循这些建议。 :)

如果 ./vmSetup.sh 1 按预期工作,这些应该工作:

parallel -j8 ./vmSetup.sh {} ::: 1 2 3 ... 100
seq 100 | parallel -j8 ./vmSetup.sh