使用变量中的变量设置比较器,然后让 shell 在每次回显时扩展这些变量

使用变量中的变量设置比较器,然后让 shell 在每次回显时扩展这些变量

这是我的脚本的一部分:

#!/bin/sh

n=1

echo "How many repetitions to run (0 = no limit)?"
read reps

if [ $reps = 0 ]; then
    while="true"
else
    while="[ $n -lt $((reps+1)) ]"
fi

echo "How much off-time in-between reps (in minutes)?"
read time

pwr_init

while $while; do
    echo "* Sending power pulse $n"
    pwr_normal
    t=$time
    echo "* Waiting for next power on"
    while [ $t -gt 0 ]; do
        echo "    $t min until next power on"; sleep 60
        t=$((t-1))
    done
    n=$((n+1))
done

while除了我正在进行的第一个循环之外,一切都完全按照我的需要进行。每当$while调用变量时,我希望 shell 扩展并检查第一个语句中定义的当时的变量if

我只能让 shell 在第一个语句时扩展变量if,但随后我得到的行为是true因为比较器始终为 true,并且在$n递增时永远不会改变,因为该$while变量已经在第一个if语句期间扩展了所有内容。

我尝试过类似的方法${!while}以及单引号和双引号的各种组合,但没有运气。我通常会收到类似$n is a bad number或 的错误bad substitution

现在我唯一能想到的就是在更多函数中添加更多语句,以便每次都if检查这一行并将其回显到语句中。while我必须相信有更好的方法来做到这一点,不过,我只是很难弄清楚(并且也找到了正确的关键字来在线搜索)。

在调试时,我使用set -x,并且可以看到比较器字符串正在以此处编写脚本的方式正确评估,但正如我提到的,它在$n递增时永远不会更新自身。

有没有人有这种类型的替代技巧,可以让生活变得更轻松一点?非常感谢任何可以对这个奇怪的变量扩展难题给出的见解!

注意 1:pwr_initpwr_normal是完整脚本中其他地方定义的函数。

注意 2:记下 shebang,它必须符合 POSIX 标准。我无法使用任何花哨的 bash 特定技术。

编辑:让它像我需要的那样发挥作用比我想象的要简单,我把我添加的内容放在下面。不过,我将这个问题留待解决,因为如果知道这种类型的变量扩展是否可以完成,那就太好了。

新脚本的check函数包含在if语句中:

#!/bin/sh

n=1

echo "How many repetitions to run (0 = no limit)?"
read reps

count=$((reps+1))

check() {
    if [ $reps = 0 ]; then
        while="true"
    else
        while="[ $n -lt $count ]"
    fi
}

echo "How much off-time in-between reps (in minutes)?"
read time

pwr_init

while $while; do
    echo "* Sending power pulse $n"
    pwr_normal
    t=$time
    echo "* Waiting for next power on"
    while [ $t -gt 0 ]; do
        echo "    $t min until next power on"; sleep 60
        t=$((t-1))
    done
    n=$((n+1))
    check
done

答案1

非常简单的解决方案:要运行“无休止”,只需将限制设置为某个非常大的数字即可。一个 32 位整数可以容纳最多 4294967296 的数字。考虑到符号位并向下舍入,我们可以假设我们至少可以计数到 2000000000。由于您在计数之间至少睡了一分钟,所以这是可行的长达,哦,大约4000年。

所以就这样做:

if [ "$reps" = 0 ]; then
    reps=2000000000
fi

并将条件硬连线为

while [ "$n" -le "$reps" ]; do ...

(如果您运行的是 64 位系统,则可以计算得更高。但这并不重要。如果您运行的是 16 位系统,那么您也可能会在其他方面被搞砸。)


简单的解决方案:只需创建一个函数来检查是否reps为零或n小于reps

keep_going() {
    [ "$reps" = 0 ] && return 0
    [ "$n" -le "$reps" ] 
}

然后提出条件

while keep_going; do ...

是的,您每次都会调用一个函数。不,这没关系,因为您最多每分钟只调用一次。(如果您确实关心速度,则不应该使用 shell 来执行此操作。)


您正在寻找的解决方案:使用eval。在对 的赋值中使用双引号while="[ $n -lt $count ]"$n和都$count将立即展开。您需要的是在赋值中使用单引号:while='[ $n -lt $count ]'然后运行字符串eval以扩展其中的变量。

while='[ $n -lt $count ]'
while eval "$while" ; do ...

答案2

您可以尝试定义不同的函数:

if [ $reps -eq 0 ]; then
    while_test() { true; }
else
    while_test() { test $1 -lt $((reps+1)); }
fi

然后

while while_test $n; do

您可以eval在 POSIX shell 中使用来处理变量中的条件:

$ cond='[ $n -lt $reps ]'
$ reps=5
$ n=4
$ eval "$cond" && echo 'n < reps' || echo 'n >= reps'
n < reps
$ n=5
$ eval "$cond" && echo 'n < reps' || echo 'n >= reps'
n >= reps

确保条件是用单引号定义的,这样变量就不会过早扩展。

相关内容