如何使 getopts 参数成为可选参数

如何使 getopts 参数成为可选参数

我写下列 bash脚本作为预算工具,可以更准确地计算(和调节)我上次购买一包香烟的时间。

除了-h打印输出之外,它还需要另一个选项:-b使用(预期的)可选参数来设置偏移量(以小时为单位)。因此-b 5记录 5 小时前购买的一包新香烟,随后的运行指示cigs.sh自那时以来已经过了多少周、多少天或多少小时。如果自上次购买一包以来经过的时间少于$threshold,则输出以红色打印;如果经过的时间超过$threshold,则输出为绿色,表示现在“可以”购买一包新香烟了。

我已经设法让所有这一切工作正常,除了一件事:目前无法指定-b 没有偏移量(即记录购买是现在进行的,而不是几个小时前进行的)。据我所知,这似乎是 的一个限制getopts:如果 中没有冒号b:h,$OPTARG 将无法填充,从而导致 case 选项中的逻辑b)失败;如果使用冒号,则无法-b在没有参数的情况下使用,从而引发以下错误:

/usr/local/bin/scripts/cigs.sh:选项需要一个参数——b
传递了未知参数:-b

这是getopts我的脚本的一部分:

while getopts "b:h" OPTION; do
    case $OPTION in
    h)
        echo "$usage"
        exit
        ;;
    b)
        offset=$OPTARG
        if [[ -n $offset && ! $offset =~ ^[0-9]+$ ]]; then 
        echo "HOURS parameter passed to -b must be an integer."
        exit
        fi
        echo -e "Bought a new $item...\nResetting timer to 0 days and $offset hours."
        offset=$((offset*60*60))
        last_bought="$(date -u +%s)"
        new_lb=$((last_bought-offset))
        echo $new_lb > $lb_file
        exit
        ;;
    *)
        echo "Unknown parameter passed: $1"
        exit 1
        ;;
    esac
done

答案1

这是一个非常有趣的问题。根据我对getopts工作原理的理解,使用单个冒号 ( :) 意味着必须设置选项值:

getopts "b:h"

如果您希望它是可选的,您需要设置两个冒号(::),并且后面没有任何内容,如下所示:

getopts "hb::"

但那hb::不起作用。

我尝试了你的脚本,这是我能找到的唯一解决方案。它改变了你的代码,getopts "b:h"所以getopts "bh"两个选项参数都是可选的。但神奇之处在于在案例中添加了更多代码b),所以整个while块现在看起来像这样:

while getopts "bh" OPTION; do
    case $OPTION in
    h)
        echo "$usage"
        exit
        ;;
    b)
        eval nextopt=\${$OPTIND}
        if [[ -n $nextopt && $nextopt != -* ]] ; then
        OPTIND=$((OPTIND + 1))
        offset=$nextopt
        else
        offset=0
        fi
        if [[ -n $offset && ! $offset =~ ^[0-9]+$ ]]; then
        echo "HOURS parameter passed to -b must be an integer."
        exit
        fi
        echo -e "Bought a new $item...\nResetting timer to 0 days and $offset hours."
        offset=$((offset*60*60))
        last_bought="$(date -u +%s)"
        new_lb=$((last_bought-offset))
        echo $new_lb > $lb_file
        exit
        ;;
    *)
        echo "Unknown parameter passed: $1"
        exit 1
        ;;
    esac
done

魔法来自于这块基于此其他 Stack Overflow 答案关于同一问题的问题:

# Check next positional parameter
eval nextopt=\${$OPTIND}
# existing or starting with dash?
if [[ -n $nextopt && $nextopt != -* ]] ; then
OPTIND=$((OPTIND + 1))
offset=$nextopt

显然,如果它能工作的话,它就不会像预期的那样干净getopts "hb::",但是它不能,所以这个临时解决方案可以工作。

如果其他更有 Bash 经验的人可以解释原因 — 或者想出更简洁的解决方案 — 他们应该发布出来。目前,这个方法有效,所以有效!

相关内容