Bash 脚本等待进程并获取返回码

Bash 脚本等待进程并获取返回码

我正在尝试创建一个脚本来启动许多后台命令。对于每个后台命令,我需要获取返回码。

我一直在尝试以下脚本:

 #!/bin/bash
set -x
pid=()
return=()


for i in 1 2
do
 echo start $i
 ssh mysql "/root/test$i.sh" &
 pid[$i]=$!
done

for i in ${#pid[@]}
do
echo ${pid[$i]}
wait ${pid[$i]}
return[$i]=$?

if [ ${return[$i]} -ne 0 ]
then
  echo mail error
fi

done

echo ${return[1]}
echo ${return[2]}

我的问题是在等待循环期间,如果第二个 pid 在第一个 pid 之前完成,我将无法获取返回代码。

我知道我可以运行 wait pid1 pid2,但是使用此命令我无法获取所有命令的返回代码。

任何想法 ?

答案1

问题更多的是你的

for i in ${#pid[@]}

这是for i in 2.

它应该是:

for i in 1 2

或者

for ((i = 1; i <= ${#pid[@]}; i++))

wait "$pid" 将要bash使用(和 POSIX shell,但不)返回作业的退出代码,zsh即使作业在wait启动时已经终止。

答案2

您可以使用临时目录来完成此操作。

# Create a temporary directory to store the statuses
dir=$(mktemp -d)

# Execute the backgrouded code. Create a file that contains the exit status.
# The filename is the PID of this group's subshell.
for i in 1 2; do
    { ssh mysql "/root/test$i.sh" ; echo "$?" > "$dir/$BASHPID" ; } &
done

# Wait for all jobs to complete
wait

# Get return information for each pid
for file in "$dir"/*; do
    printf 'PID %d returned %d\n' "${file##*/}" "$(<"$file")"
done

# Remove the temporary directory
rm -r "$dir"

答案3

没有临时文件的通用实现。

#!/usr/bin/env bash

## associative array for job status
declare -A JOBS

## run command in the background
background() {
  eval $1 & JOBS[$!]="$1"
}

## check exit status of each job
## preserve exit status in ${JOBS}
## returns 1 if any job failed
reap() {
  local cmd
  local status=0
  for pid in ${!JOBS[@]}; do
    cmd=${JOBS[${pid}]}
    wait ${pid} ; JOBS[${pid}]=$?
    if [[ ${JOBS[${pid}]} -ne 0 ]]; then
      status=${JOBS[${pid}]}
      echo -e "[${pid}] Exited with status: ${status}\n${cmd}"
    fi
  done
  return ${status}
}

background 'sleep 1 ; false'
background 'sleep 3 ; true'
background 'sleep 2 ; exit 5'
background 'sleep 5 ; true'

reap || echo "Ooops! Some jobs failed"

答案4

Stéphane 的回答很好,但我更喜欢

for i in ${!pid[@]}
do
    wait "${pid[i]}"
    return_status[i]=$?
    unset "pid[$i]"
done

它将迭代pid数组的键,无论哪些条目仍然存在,因此您可以调整它,跳出循环,然后重新启动整个循环,它就会正常工作。并且i一开始就不需要连续的值。

当然,如果您正在处理数千个进程,那么当您拥有非稀疏列表时,Stépane 的方法可能会更有效。

相关内容