使用 || 压缩 bash 函数操作员重试命令

使用 || 压缩 bash 函数操作员重试命令

我正在尝试生成一个类似于 Travis CI 的函数的非常紧凑的版本travis_retry。该函数基本上应该是一个高阶函数,尝试将提供的参数作为命令三次,如果重复失败则失败(返回 1)。

以下是我所做的一些尝试,但都没有达到我的预期:

retry() {
    "${@}" || "${@}" || "${@}"
}

这似乎适用于简单的命令(例如echo ciao),但对于任何重要的表达式都失败:

$ retry if true; then echo hello; fi; false
bash: syntax error near unexpected token `then'

我预计该函数会打印hello三次并返回1(由于false最后被评估)。

第二次尝试如下:

retry() {
    $* || $* || $*
}

这看起来就像前一个一样。我还尝试了第三个版本eval,但当然它不起作用:此时很明显我在这里缺少一些基础知识,我想理解而不是继续尝试。

答案1

如果使用函数,则只能传递多个字符串参数,因此只能将其解释为简单命令的参数。您可以将任意 shell 代码作为一个字符串传递,但必须对该代码进行求值,并且必须将其引用,以便将其作为一个文字字符串传递给函数。

retry() { eval " $1" || eval " $1" || eval " $1"; }
retry 'if true; then echo hello; fi; false'

或者你的:

retry() { "$@" || "$@" || "$@"; }

称为:

retry eval 'if true; then echo hello; fi; false'

但是,使用别名,您可以执行以下操作:

alias 'retry{{={
  attempts=0
  until {' '}}=
  }; do
    if (( ++attempts >= 3 )); then
      echo >&2 "Giving up after $attempts attempts"
      ! break
    fi
  done
}'

进而:

bash-5.0$ retry{{ echo test; false; }}
test
test
test
Giving up after 3 attempts

这是可行的,因为在完全解析代码之前别名会在读取代码时展开,有点像 C 中的预处理器宏。

答案2

另一种选择是使用重试命令而不是 bash 第一原则。

在这里,我们重试一个每次都会失败的不平凡的表达式,在三次尝试后放弃。

~$ ./retry --times 3 -- sh -c "echo hello; echo there; false"
hello
there
retry: sh returned 1, backing off for 10 seconds and trying again...
hello
there
retry: sh returned 1, backing off for 10 seconds and trying again...
hello
there
retry: sh returned 1, backing off for 10 seconds and trying again...

https://github.com/minfrin/retry

在最新的 Debian、Ubuntu 和 Nix 中开箱即用。

相关内容