如何在bash中多次调用getopts

发布于 2024-08-19 22:11:19 字数 687 浏览 9 评论 0原文

我有一个通用库,可以从多个解析命令行选项的脚本中使用它,但是我也希望我的个人脚本也能够处理参数...... 例如

common.sh:

function get_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":ab:" optionName; do
       [ ... processing code ... ]
    done
}

a.sh

. ./common.sh

function get_local_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":xy:" optionName; do
       [ ... processing code ... ]
    done
}

get_local_options $*
OPTIND=1
get_options $*

问题是,如果我用以下命令调用 a.sh:

a.sh -x -y foo -a -b bar

get_options 会在“foo”处停止处理,因为它会在第一个“非选项”处停止

有什么方法可以解决这个问题而不需要我自己重写吗?

I have a common library that I use from several scripts that parses command line options, however I also want my individual scripts to be able to process arguments as well...
e.g.

common.sh:

function get_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":ab:" optionName; do
       [ ... processing code ... ]
    done
}

a.sh

. ./common.sh

function get_local_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":xy:" optionName; do
       [ ... processing code ... ]
    done
}

get_local_options $*
OPTIND=1
get_options $*

The problem si that if I call a.sh with:

a.sh -x -y foo -a -b bar

get_options stops processing at "foo" as it stops at the first "non-option"

Any way around this without rewriting things myself?

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

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

发布评论

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

评论(5

独自唱情﹋歌 2024-08-26 22:11:19
foo() {
  unset OPTIND
  while getopts ...
  do
  done
}
foo() {
  unset OPTIND
  while getopts ...
  do
  done
}
梦在夏天 2024-08-26 22:11:19

只需让您的通用脚本提供通用选项和处理这些选项的函数的列表,然后让其他脚本将它们可以处理的任何选项添加到选项列表中:

common.sh

OPTS="ab:hv"

common_usage() {
  echo "Common options:"
  echo "  -a     ..."
  echo "  -b arg ..."
  echo "  -h     Show usage"
  echo "  -v     Be verbose"
}

handle_common_opts() {
  case "$1" in
    a) handle_opt_a ;;
    b) handle_opt_b "$2" ;;
    h) usage; exit 0 ;;
    v) be_verbose ;;
  esac
}

a.sh

. common.sh

OPTS="${OPTS}xy:"

usage() {
  echo "Usage: $PROG [options] [arguments...]"
  echo
  common_usage
  echo
  echo "Specific options:"
  echo "  -x     ..."
  echo "  -y arg ..."
}

while getopts "$OPTS" OPT; do
  handle_common_opts "$OPT" "$OPTARG"
  case "$OPT" in
    x) handle_opt_x ;;
    y) handle_opt_y "$OPTARG" ;;
    \?) usage; exit 1 ;;
  esac
done
shift $((OPTIND-1))

handle_remaining_args "$@"

这具有以下优点:无需播放getopts 的内部管理技巧,用户可以按任意顺序指定常见和特定选项,它在一个地方处理无法识别的选项,并且还使使用功能更易于维护。

Just have your common script provide a list of common options and functions for handling those, then have the other scripts add to the option list any options which they can handle:

common.sh

OPTS="ab:hv"

common_usage() {
  echo "Common options:"
  echo "  -a     ..."
  echo "  -b arg ..."
  echo "  -h     Show usage"
  echo "  -v     Be verbose"
}

handle_common_opts() {
  case "$1" in
    a) handle_opt_a ;;
    b) handle_opt_b "$2" ;;
    h) usage; exit 0 ;;
    v) be_verbose ;;
  esac
}

a.sh

. common.sh

OPTS="${OPTS}xy:"

usage() {
  echo "Usage: $PROG [options] [arguments...]"
  echo
  common_usage
  echo
  echo "Specific options:"
  echo "  -x     ..."
  echo "  -y arg ..."
}

while getopts "$OPTS" OPT; do
  handle_common_opts "$OPT" "$OPTARG"
  case "$OPT" in
    x) handle_opt_x ;;
    y) handle_opt_y "$OPTARG" ;;
    \?) usage; exit 1 ;;
  esac
done
shift $((OPTIND-1))

handle_remaining_args "$@"

This has the following advantages: no need to play tricks with the internal houskeeping of getopts, the user can specify common and specific options in any order, it handles unrecognized options in a single place, and also makes the usage functions easier to maintain.

云淡月浅 2024-08-26 22:11:19

我设法让它工作,不确定这是否是你想要的:

$ cat common.sh
function get_options {
    while getopts ":ab:" optionName
    do
        echo "get_options: OPTARG: $OPTARG, optionName: $optionName"
    done
}
$ cat a.sh
#!/bin/bash

. ./common.sh

function get_local_options {
    while getopts ":xy:" optionName; do
        case $optionName in
            x|y) 
                echo "get_local_options: OPTARG: $OPTARG, optionName: $optionName"
                last=$OPTIND;;
            *) echo "get_local_options, done with $optionName"
                break;;
        esac;
    done
}

last=1
get_local_options $*
shift $(($last - 1))
OPTIND=1
get_options $*
$ ./a.sh -x -y foo -a -b bar
get_local_options: OPTARG: , optionName: x
get_local_options: OPTARG: foo, optionName: y
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b
$ ./a.sh -a -b bar
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b

I managed to get this to work, not sure if this is what you want:

$ cat common.sh
function get_options {
    while getopts ":ab:" optionName
    do
        echo "get_options: OPTARG: $OPTARG, optionName: $optionName"
    done
}
$ cat a.sh
#!/bin/bash

. ./common.sh

function get_local_options {
    while getopts ":xy:" optionName; do
        case $optionName in
            x|y) 
                echo "get_local_options: OPTARG: $OPTARG, optionName: $optionName"
                last=$OPTIND;;
            *) echo "get_local_options, done with $optionName"
                break;;
        esac;
    done
}

last=1
get_local_options $*
shift $(($last - 1))
OPTIND=1
get_options $*
$ ./a.sh -x -y foo -a -b bar
get_local_options: OPTARG: , optionName: x
get_local_options: OPTARG: foo, optionName: y
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b
$ ./a.sh -a -b bar
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b
樱花落人离去 2024-08-26 22:11:19

我认为您需要让您的第一个 getopts 处理器构建它未处理的命令行元素列表。此外,它还必须忽略 getopts 调用的返回值:getopts 可能会返回非零值,因为它遇到了一个不知道如何处理的命令行开关参数。所以第一个 getopts 处理器需要走到命令行的末尾。你不能相信它自己对选择何时停止、争论何时开始的判断。 (好吧,一旦你连续得到两个参数,你可能可以跳过其余的,但我将把它作为练习。)

像这样,然后:

#!/bin/bash

UNHANDLED=()

function getxy {
  while ((OPTIND<=$#)); do
    if getopts ":xy:" opt; then
      case $opt in
        x|y) echo "getxy opt=
lt;$opt> OPTARG=<$OPTARG>";;
        *) UNHANDLED+=(-$OPTARG);;
      esac
    else
        UNHANDLED+=(${!OPTIND})
        let OPTIND++
    fi
  done
}

function getab {
  while getopts ":ab:" opt; do
  case $opt in
    a|b) echo "getab opt=
lt;$opt> OPTARG=<$OPTARG>";;
    *) echo "getab * opt=<$opt> OPTARG=<$OPTARG>";;
  esac
  done
}

echo "--- getxy ---"
OPTIND=1
getxy "$@"
# now we reset OPTIND and parse again using the UNHANDLED array
echo "--- getab ---"
OPTIND=1
set -- "${UNHANDLED[@]}"
getab "$@"
# now we get remaining args
shift $((OPTIND-1))
for arg; do
    echo "arg=<$arg>"
done

I think you'll need to have your first getopts processor build a list of command line elements it didn't handle. Also, it will have to ignore the return value of the getopts call: getopts might return non-zero because it's hitting an argument to a command-line switch that it doesn't know how to process. So the first getopts processor needs to go to the end of the command-line. You can't trust its own judgments about when the options have stopped and the arguments have started. (Well, once you get two arguments in a row you could presumably skip the rest, but I'll leave that as an exercise.)

Something like this, then:

#!/bin/bash

UNHANDLED=()

function getxy {
  while ((OPTIND<=$#)); do
    if getopts ":xy:" opt; then
      case $opt in
        x|y) echo "getxy opt=
lt;$opt> OPTARG=<$OPTARG>";;
        *) UNHANDLED+=(-$OPTARG);;
      esac
    else
        UNHANDLED+=(${!OPTIND})
        let OPTIND++
    fi
  done
}

function getab {
  while getopts ":ab:" opt; do
  case $opt in
    a|b) echo "getab opt=
lt;$opt> OPTARG=<$OPTARG>";;
    *) echo "getab * opt=<$opt> OPTARG=<$OPTARG>";;
  esac
  done
}

echo "--- getxy ---"
OPTIND=1
getxy "$@"
# now we reset OPTIND and parse again using the UNHANDLED array
echo "--- getab ---"
OPTIND=1
set -- "${UNHANDLED[@]}"
getab "$@"
# now we get remaining args
shift $((OPTIND-1))
for arg; do
    echo "arg=<$arg>"
done
鹿港小镇 2024-08-26 22:11:19

这里有一些很好的答案,但我认为有一个简单的解决方案:您需要增加 OPTIND 而不是将其重置为 1。即

a.sh

. ./common.sh

function get_local_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":xy:" optionName; do
       [ ... processing code ... ]
    done
}

get_local_options $*
(( OPTIND++ ))
get_options $*

这样您将跳过单词 foo 在参数列表中。

There are some good answers here but I think there is a simple solution: you need to increment OPTIND instead of resetting it to 1. i.e.

a.sh

. ./common.sh

function get_local_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":xy:" optionName; do
       [ ... processing code ... ]
    done
}

get_local_options $*
(( OPTIND++ ))
get_options $*

In this way you will skip the word foo in the args list.

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