Bash 脚本中的可选参数

Optional arguments in Bash script

我正在尝试编写一个具有可选参数的函数,使得可选参数后跟 -x 之类的东西,例如

my_function man_arg1 man_arg2 -n opt_arg1 -o opt_arg2 -x opt_arg3

我希望它也支持像

这样的调用
my_function man_arg1 man_arg2 -x opt_arg

在其他问题中,我看到有人建议使用 getopts,但在这些答案中,您似乎必须在调用函数时指定所有可选参数?此外,我仍然不清楚您将如何使用 getopts。

我不确定我是否正确理解了问题,如果我没有回答,抱歉...

您可以使用(例如)getopt(1),如下所示,这将允许 -x 选项位于任何位置。 请注意,可选参数 (man_arg*) 也可以在任何地方。

#! /usr/bin/bash

# CMD is the shell-script name without directory (basename)
CMD="${0##*/}"

my_function() {
    # Some comments added per OP request

    # below variable is used by getopt's "-o" option (short options).
    # each letter is a possible option; colon following a letter means this
    # option will take a parameter. See getopt(1) for more details.
    SOPTS="n:o:x:"

    # output of getopt is assigned to TMP. For example, when calling :
    # my_function -n n1 a b -o o1 -x x1 c d
    # TMP will contain: "-n 'n1' -o 'o1' -x 'x1' -- 'a' 'b' 'c' 'd'"
    TMP=$(getopt -o "$SOPTS" -n "$CMD" -- "$@") || exit 1

    # assign TMP to positional parameters , , etc...
    eval set -- "$TMP"
    unset TMP

    while true; do
        case "" in
            -n|-o)
                printf "[%s] argument: %s\n" "" ""
                shift
                ;;
            -x)
                printf "[-x] argument: %s\n" ""
                shift
                ;;
            --)                                       # end of options
                shift
                break
                ;;

        esac
        shift
    done

    nargs=$#
    printf "remaining %d args :\n" "$nargs"
    for ((i=0; i<nargs; ++i)); do
        printf "%d: %s\n" $((i + 1)) ""
        shift
    done
}

my_function "$@"

示例:

br@lorien:~$ ./test-getopt.bash man_arg1 man_arg2 -n opt_arg1 -o opt_arg2 -x opt_arg3
[-n] argument: opt_arg1
[-o] argument: opt_arg2
[-x] argument: opt_arg3
remaining 2 args :
1: man_arg1
2: man_arg2

br@lorien:~$ ./test-getopt.bash man_arg1 man_arg2 -n opt_arg1 -o opt_arg2 -x opt_arg3 man_arg3 man_arg4
[-n] argument: opt_arg1
[-o] argument: opt_arg2
[-x] argument: opt_arg3
remaining 4 args :
1: man_arg1
2: man_arg2
3: man_arg3
4: man_arg4

br@lorien:~$ ./test-getopt.bash man_arg1 man_arg2 -x opt_arg
[-x] argument: opt_arg
remaining 2 args :
1: man_arg1
2: man_arg2

br@lorien:~$ ./test-getopt.bash -x opt_arg4 man_arg2 -n opt_arg1 -x opt_arg3 man_arg3 man_arg4
[-x] argument: opt_arg4
[-n] argument: opt_arg1
[-x] argument: opt_arg3
remaining 3 args :
1: man_arg2
2: man_arg3
3: man_arg4

编辑:将代码重写为函数,如问题所述。

POSIX 约定是命令的 non-option 参数必须在 所有选项和选项参数之后。 POSIX getopts 实用程序是围绕此约定设计的,因此 如果您不想坚持约定俗成的参数顺序,那么 getopts 并不适合.在那种情况下,getopts 可能仍然可以工作,但我不会尝试这样做。

如果您愿意依赖 the GNU version of the getopt utility(不要与 getopts 混淆,与 s 混淆),那么它可以容纳您。默认情况下,它可以识别命令行中任何位置的选项,包括 non-option 参数之后的选项。这可能看起来像这样:

args=$(getopt --name "[=10=]" --options n:o:x: -- "$@")
eval set -- "$args"

while [[ $# -gt 0 ]]; do
  case "" in
    -n) n_argument=; shift 2;;
    -o) o_argument=; shift 2;;
    -x) x_argument=; shift 2;;
    *)
      # Handle non-option argument  ...
      shift
    ;;
  esac
done

但请注意,您可以在没有 getopt 的情况下做同样的事情。 getopt(或getopts)为您做的是:

  • 标准化联合选项(-abc 等同于 -a -b -c;仅适用于不需要参数的选项)
  • 正常化选项 /参数分离(-xfoo等于-x foo当option x获取参数)
  • 认识到并适应​​参数 -- 的重要性,因为它表明只有 non-option 个参数遵循
  • 识别参数中的错误,尤其是在需要的地方遗漏了选项参数
  • 类似于上面的 GNU-style 长选项(仅限 GNU getopt

如果你对这些都不感兴趣,或者如果你愿意为你想要的那些人推出自己的东西,或者如果你不能依赖 GNU 版本的 getopt 然后你可以使用类似于上面的循环,而不首先使用 getopt 来处理参数列表,并且不涉及 getopts.