Bash 脚本中的可选参数

发布于 2025-01-12 00:18:02 字数 330 浏览 1 评论 0原文

我正在尝试编写一个具有可选参数的函数,例如,可选参数后面跟着类似 -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 来实现这一点。

I am trying to write a function that has optional arguments in a way such that the optional arguments are follow by something like -x, for example

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

and I want this to also support callings like

my_function man_arg1 man_arg2 -x opt_arg

In other questions I see people suggesting using getopts but in those answers it seems like you have to specify all the optional arguments when calling the function? Also it still seems unclear to me how you would use getopts for this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

九八野马 2025-01-19 00:18:02

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

您可以使用(例如)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 $1, $2, etc...
    eval set -- "$TMP"
    unset TMP

    while true; do
        case "$1" in
            -n|-o)
                printf "[%s] argument: %s\n" "$1" "$2"
                shift
                ;;
            -x)
                printf "[-x] argument: %s\n" "$2"
                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)) "$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

编辑:按照问题的要求将代码重写为函数。

I am not sure I understood the question correctly, sorry if I don't answer it...

You could use (for example) getopt(1), as below, which will allow -x option to be anywhere.
Please note that optional arguments (man_arg*) can also be anywhere.

#! /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 $1, $2, etc...
    eval set -- "$TMP"
    unset TMP

    while true; do
        case "$1" in
            -n|-o)
                printf "[%s] argument: %s\n" "$1" "$2"
                shift
                ;;
            -x)
                printf "[-x] argument: %s\n" "$2"
                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)) "$1"
        shift
    done
}

my_function "$@"

Examples:

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

EDIT: Rewrote the code into a function, as asked in question.

爱她像谁 2025-01-19 00:18:02

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

如果您愿意依赖 GNU 版本的 getopt 实用程序(不要与 getopts 混淆,带有 s),那么它就可以满足您的需求。默认情况下,它识别命令行上任何位置的选项,包括非选项参数之后的选项。可能看起来像这样:

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

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

但请注意,您可以在没有 getopt 的情况下执行相同的操作。 getopt (或 getopts) 为您做的是:

  • 标准化联动选项(-abc 相当于 -a -b -c< /code>; 仅适用于不需要参数的选项)
  • 规范化选项/参数分离(当选项 x 时,-xfoo 相当于 -x foo接受论证)
  • 认识并适应论证的重要性-- 用于指示仅跟随非选项参数
  • 识别参数中的错误,特别是省略选项参数,其中需要
  • 类似于上面的 GNU 样式长选项 (GNU getopt仅限

如果您对其中任何一个都不感兴趣,或者您愿意自己开发您想要的那些,或者如果您不能依赖 GNU 版本的 < code>getopt 那么你可以使用类似于上述操作无需首先使用 getopt 来调整参数列表,也不涉及 getopts

POSIX convention is that non-option arguments to a command must come after all options and option arguments. The POSIX getopts utility is designed around this convention, so if you don't want to insist on conventional argument order then getopts is not a good fit. It might still be possible to make getopts work in that case, but I wouldn't try to do so.

If you are willing to depend on the GNU version of the getopt utility (not to be confused with getopts, with an s), then it can accommodate you. By default, it recognizes options anywhere on the command line, including after non-option arguments. That might look like this:

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

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

But note that you can do much the same without getopt. What getopt (or getopts) does for you is:

  • normalize ganged options (-abc equivalent to -a -b -c; only for options that do not require arguments)
  • normalize option / argument separation (-xfoo equivalent to -x foo when option x takes an argument)
  • recognize and accommodate the significance of argument -- for indicating that only non-option arguments follow
  • recognize errors in the arguments, especially the omission of option arguments where they are required
  • similar to the above for GNU-style long options (GNU getopt only)

If you're not interested in any of that, or if you're willing to roll your own for the ones of those that you want, or if you can't rely on having the GNU version of getopt then you can use a loop similar to the above without first using getopt to massage the argument list, and without involving getopts.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文