1

我正在创建一个涉及解析参数的 bash 脚本。用法是:

$ ./my_script.sh -a ARG_1 -b ARG_2 [-c LIST_OF_ARGS...]

使用getopts我能够解析-a-b获取它们各自的值ARG_1ARG_2. 当且仅当用户将-c其作为最后一个参数时,我还能够获取-c并创建一个包含所有值的列表LIST_OF_ARGS...

但我不想强迫用户-c作为最后一个标志插入。例如,如果可以以这种方式调用脚本,那就太好了:

$ ./my_script.sh -b ARG_2 -c V1 V2 V3 -a ARG_1

这是我当前的代码:

while getopts a:b:c opt
do
    case $opt in
        a)
            A_FLAG=$OPTARG
            ;;
        b)
            B_FLAG=$OPTARG
            ;;
        c)
            # Handle values as regular expressions
            args=("$@")
            C_LIST=()
            for (( i=$OPTIND-1 ; i <= $#-1 ; i++ ))
            do
                C_LIST=("${C_LIST[@]}" ${args[$i]})
            done
            ;;
        ?)
            usage
            ;;
    esac
done
4

3 回答 3

1

在我的系统上,我有一个/usr/share/doc/util-linux/examples/getopt-parse.bash文件。它将结果getopt放入变量中,并将位置参数设置为该变量。然后使用switch与您的类似的,但shift在找到时用于删除参数。

您可以做类似的事情,但在您获得选项或用完参数之前,请-c使用您的选项。shift


或者使用当前的解决方案可能就足够了,但请记住在循环之后设置变量。OPTIND

于 2013-07-25T13:28:51.483 回答
1

您需要将-c标志的检测与与之相关的处理分开。例如,类似:

while getopts a:b:c opt
do
    case $opt in
        a)
            A_FLAG=$OPTARG
            ;;
        b)
            B_FLAG=$OPTARG
            ;;
        c)
            C_FLAG=1
            ;;
        ?)
            usage
            ;;
    esac
done

# discard all of our options.
shift `expr $OPTIND - 1`

if [ "$C_FLAG" = 1 ]; then
            # Handle values as regular expressions
            args=("$@")
            C_LIST=()
            for (( i=0 ; i <= $#-1 ; i++ ))
            do
                C_LIST=("${C_LIST[@]}" ${args[$i]})
            done
fi

处理完所有命令行选项之前,此脚本不会收集所有非选项参数。

于 2013-07-25T13:28:58.917 回答
1

这里有一个问题:为什么有一个 -c 选项?

如果完整用法涉及值列表,为什么不只是没有 -c 选项,只允许 -a 和 -b 选项,而其余的是常规参数,如 in ./myscript.sh -a ARG_1 -b ARG_2 [argument ...],其中任何参数都是可选的(如 -c 选项及其参数在您的使用示例中吗?

然后你的问题变成“我如何穿插程序选项和参数”,我会回答:“你不应该这样做,但无论如何要实现这一点,自己解析命令行;getopts不会按照你想要的方式工作否则。”

当然,解析是困难的方式。另一种可能性是将 -c 之后的值添加到列表中,只要您没有遇到另一个选项或选项的结尾:

C_LIST=()
while getopts a:b:c: opt; do
    #Skipping code...
    c)
        C_LIST+="$OPTARG"
        shift $(expr $OPTIND - 1)
        while [ -n "$1" ] && [ $(printf "%s" "$1" | grep -- '^[^-]') ]; do
            C_LIST+="$1"
            shift
        done
        OPTIND=1
        ;;

getopts 的行为是模仿的:即使 OPTARG 以 '-' 字符开头,它仍然保留,但在 OPTARG 之后,任何以 '-' 字符开头的字符串都可能只是一个无效选项,例如 -n。我使用 printf 而不是 echo,因为某些版本的 echo,例如 bash 内置的版本,有一个 -e 选项,它可能允许也可能不允许循环继续,这是不希望的。grep 表达式应该阻止这种情况,但是谁知道那个版本的 echo 是否允许 -e'hello',这会导致 grep 因为看到“hello”而成功?虽然可能没有必要,但为什么要冒险?

就个人而言,如果可以的话,我会避免这种行为,但我也不明白你为什么首先要求这种行为。如果我要推荐任何东西,我会建议/path/to/script -a ARG_1 -b ARG_2 [argument ...]比任何其他可能的实现选择更常见的样式。

于 2013-07-25T23:57:07.067 回答