如何使用 bash 在一组参数上并行执行多个命令,如果其中至少一个失败则失败

How do I execute multiple commands in parallel on an array of parameters with bash, and fail if at least one of them failed

我有一个 bash 脚本,其中的函数需要 运行 与不同的参数并行。 我需要知道是否至少有一个执行失败(返回非零)——不管有多少失败。

该命令接受一组执行参数。 由于高负载,我需要将并发限制为 4 个并发 运行。 我还需要在父进程(运行s bash 脚本)

中打印日志

这是我运行宁的功能:

function run_and_retry {
  EXIT_STATUS=0
  $COMMAND || EXIT_STATUS=$?

  if [ $EXIT_STATUS -ne 0 ]; then
    EXIT_STATUS=0
    $COMMAND || EXIT_STATUS=$?

  fi

  return $EXIT_STATUS
}

我尝试过使用 GNU parallel 和 xargs,但都遇到了问题。

使用 xargs:(无法从中获取退出状态,当我在 TravisCI 中 运行 时它也不起作用)

PARAMETERS=(first-parameter second-parameter third-parameter)
export -f run_and_retry
echo "${PARAMETERS[@]}" | xargs -P 4 -n 1 -I {} bash -c "run_and_retry {}"

与 GNU 并行:

PARAMETERS=(first-parameter second-parameter third-parameter)
export -f run_and_retry
parallel -j 4 -k --lb 2 run_and_retry {} ::: echo "${PARAMETERS[@]}" 

I need to know if at least one of the executions failed (returned non-zero)

来自posix xargs

EXIT STATUS

1-125
A command line meeting the specified requirements could not be assembled, one or more of the invocations of utility returned a non-zero exit status, or some other error occurred.

man xargs好像有点不一样:

EXIT STATUS

123 if any invocation of the command exited with status 1-125

但我会检查命令的 return 状态和 return 函数中的预定义数字(例如 1)来处理它。

parameters=(1 2 3 fail)

func() { 
    COMMAND=sleep
    # I guess OP intends to try running COMMAND twice
    if ! "$COMMAND" 0."" && ! "$COMMAND" 0.""; then
        return 1
    fi
}

export -f func
if printf "%s[=10=]" "${parameters[@]}" | xargs -0 -P4 -n1 -t -- bash -c 'func ' -- ; then
   echo "Success!"
else
   echo "Error!"
fi

在线版本可在 tutorialspoint 获得。

好吧,我们甚至可以手动计算孩子的数量,使用 wait -n 变得非常简单。来自 Whosebug - WAIT for “1 of many process” to finish:

bash 4.3 added a -n flag to the built-in wait command, which causes the script to wait for the next child to complete.

所以我们可以:

cnt=0
failed=false
for i in "${parameters[@]}"; do
    ( func "$i" ) &
    if (( cnt < 4 )); then
        cnt=$((cnt+1))
    else
        # handle more then 4 processes
        if ! wait -n; then
           failed=true
        fi
    fi
done
# handle still running processes after all have been forked
for i in $(seq $cnt); do
    if ! wait -n; then
        failed=true
    fi
done

if "$failed"; then
    echo "One of the jobs failed!"
fi

所以接近正确的 GNU Parallel 语法:

COMMAND=echo
PARAMETERS=(first-parameter second-parameter third-parameter)
parallel -j 4 -k --retries 2 "$COMMAND" {} ::: "${PARAMETERS[@]}" ||
  echo $? commands failed. More than 99 if $? = 100

或者如果你真的坚持自己重试:

PARAMETERS=(first-parameter second-parameter third-parameter)
export -f run_and_retry
parallel -j 4 -k run_and_retry {} ::: "${PARAMETERS[@]}" ||
  echo One or more commands failed