使用值列表解析标志

时间:2013-07-25 13:19:26

标签: bash shell getopts

我正在创建一个涉及解析参数的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

3 个答案:

答案 0 :(得分:1)

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

您可以执行类似操作,但是对于-c选项,请使用shift,直到您获得选项或用完参数。


或者它可能足以让您使用当前的解决方案,但请记住在循环后设置 OPTIND变量。

答案 1 :(得分: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

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

答案 2 :(得分:1)

这是一个问题:为什么要使用-c选项?

如果完整用法涉及值列表,为什么不只是没有-c选项并且只允许-a和-b选项,而其余的是./myscript.sh -a ARG_1 -b ARG_2 [argument ...]中的常规args,其中任何参数都是可选的(比如-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内置的echo)有一个-e选项,可能允许或不允许循环继续,这是不希望的。 grep表达式应该阻止这个,但是谁知道这个版本的echo是否允许-e'hello',这会导致grep成功,因为它看到“你好”?虽然可能没必要,为什么要冒险?

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