【问题标题】: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 对一组参数并行执行多个命令,如果其中至少一个失败则失败
【发布时间】:2019-05-29 21:12:58
【问题描述】:

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

该命令接受一个参数数组来执行。 由于高负载,我需要将并发限制为 4 个并发运行。 我还需要在父进程(运行 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 并行和 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[@]}" 

【问题讨论】:

  • -j 并行选项需要一个参数,例如。 parallel -j 4。什么是COMMAND,它是如何分配的?
  • 谢谢,已解决
  • run_and_retry 函数似乎是一种非常迂回的说法 $COMMAND || $COMMAND || return

标签: bash parallel-processing xargs gnu-parallel


【解决方案1】:

如此接近于正确理解 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

【讨论】:

  • 我遇到了退出状态的问题 - 脚本失败,因为命令在第一次尝试时失败,但在第二次尝试时通过了.. 你知道如何使 GNU 并行失败仅当命令失败两次?我所做的是:parallel -j 4 -k --retries 2 "$COMMAND{}" ::: "${PARAMETERS[@]}" || FAIL=1
  • @AviaEyal GNU Parallel 尝试了两次。如果命令两次都失败,它会打印失败的输出并返回错误。使用-u 运行以查看两次失败尝试的输出。
【解决方案2】:

我需要知道是否至少有一个执行失败(返回非零)

来自posix xargs

退出状态

1-125
无法组装满足指定要求的命令行,实用程序的一个或多个调用返回非零退出状态,或发生其他错误。

man xargs 似乎有点不同:

退出状态

如果命令的任何调用以状态 1-125 退出,则为 123

但我会检查命令的返回状态并从函数返回一个预定义的数字(例如 1)来处理它。

parameters=(1 2 3 fail)

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

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

tutorialspoint 提供实时版本。

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

bash 4.3 在内置的等待命令中添加了一个 -n 标志,这会导致脚本等待下一个子进程完成。

所以我们可以:

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

【讨论】:

  • 感谢您的回答!我正在运行的命令是“./node_modules/protractor/bin/protractor ./protractor.conf.js --specs=test/e2e/login/login.spec.js”,我收到@987654329的错误@。如果我将命令复制粘贴到 shell 中,它就可以工作,而不是在脚本中。知道为什么吗?
  • No such file or directory 不应该是一个完整的错误,shell 应该告诉它找不到哪个文件或目录。您可能对quoting 感兴趣。可能你想使用 bash arraysCOMMAND=(echo 1 2 3); "${COMMAND[@]}" "$additional_arg"
  • 好酷,当我删除引号时,它使命令运行。但即使命令本身通过,该功能仍然失败。我仍然有这个错误:environment: line 2: Report: command not found,我不知道为什么,我没有看到命令本身的日志
猜你喜欢
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-25
  • 2017-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多