我正在尝试生成一个类似于 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 中开箱即用。