How to allow non-optional arguments to be in front of optional arguments using getopts in Bash?

Cpp*_*ner 3 bash getopts

I know how to handle optional arguments using getopts in Bash

#!/bin/bash

while getopts ":a:p:drh" opt; do
  case "$opt" in
    a) echo $OPTARG;;
  esac
done
Run Code Online (Sandbox Code Playgroud)

But if my script expects ./script.sh arg1 [options], how do I tell getopts to skip arg1?

$ ./script.sh -a 1234
1234
$ ./script.sh arg1 -a 1234
$ ./script.sh arg1
$ ./script.sh -a 1234 arg1
1234
Run Code Online (Sandbox Code Playgroud)

As I see I can handle the argument if the argument is put in the last position. What "regex" do I need in the getopts to allow my positional argument to be in front of the optional argument?

The suggestions from How to allow non-option arguments in any order to getopt? seems to be reordering the arguments.

alv*_*its 5

这就是我要怎么做。您可以在任何地方放置多个非可选参数。

#!/bin/bash

while [ $# -gt 0]; do
  while getopts ":a:p:drh" opt; do
    case "$opt" in
      a) echo $OPTARG; shift;;
      p) echo $OPTARG; shift;;
      d) echo Option d;;
      r) echo Option r;;
      h) echo Option h;;
      \?) echo unknown Option;;
      :) echo missing required parameter for Option $OPT;;
    esac
    shift
    OPTIND=1
  done
  if [ $# -gt 0 ]; then
    POSITIONALPARAM=(${POSITIONALPARAM[@]} $1)
    shift
    OPTIND=1
  fi
done

echo ${POSITIONALPARAM[@]}
Run Code Online (Sandbox Code Playgroud)

内部循环解析参数。每当遇到非选项参数时,它将退出内循环。外部循环将抓取下一个非选项参数。内循环将在非选项参数被移除后启动,然后继续读取下一个可选参数,依此类推。

正如Chrono Kitsune建议我解释的那样,移位删除了第一个参数(可选或不可选)并将所有内容向左移动一个位置。$1被移除、$2变成$1$3变成$2等等。这使脚本可以控制移动非可选参数,直到它变为$1

无论shift和复位OPTIND使这成为可能。感谢Chrono Kitsune建议将 OPTIND 重置为 1 而不是 0。

./sample.bash -a one -p two more -d sample

输出:

one
two
Option d
more sample
Run Code Online (Sandbox Code Playgroud)

现在让我们使用不同位置的参数调用脚本。

./sample.bash more -a one -p two -d sample

输出:

one
two
Option d
more sample
Run Code Online (Sandbox Code Playgroud)