如何在 Bash 脚本中解析选项,在“--”后面留下无法识别的选项?

如何在 Bash 脚本中解析选项,在“--”后面留下无法识别的选项?

我正在寻找一种在 Bash 脚本中进行选项解析的方法(允许短参数和长参数,如获取选择确实)在第一个无法识别的参数处停止解析,放置一个--在第一个无法识别的参数之前,然后将其余参数复制到输出字符串。

例如,这是我想要的行为:

% OPTIONS=("-u" "name1" "--username=name2" "-x" "a" -u "b" "c")
% getopt -o u: -l username: -n programname -- "${OPTIONS[@]}"
-u 'name1' --username 'name2' -- -x 'a' -u 'b' 'c'
%

实用程序 getopt 不会以这种方式工作,而是发出以下内容:

programname: invalid option -- 'x'
-u 'name1' --username 'name2' -u 'b' -- 'a' 'c'

请注意,我不希望对无法识别的选项后面的参数进行重新排序,就好像它们已被识别一样,如上面第二个所示-u选项。我希望有人能找到一个解决方案,给出我在上面第一个代码块中演示的结果。有任何想法吗?

环境:bash 4.2.46(1)、CentOS 7.2 @3.10.0-327.36.1.el7.x86_64。

要求:CentOS 7.2 Minimal,无需安装任何其他软件,所有代码均以 Bash 脚本编写。传递给选项解析器的选项不需要包含--在它们中(也就是说,解析的终止应该是自动的)。

答案1

解决这个问题需要一些其他的东西获取选择因为获取选择重新排列它发现与选项规范匹配的选项,并在无法识别的选项上终止。 Bash 内置获取选择来救援,但需要能够处理长期选择。在帖子中Arvid Requate 和 TomRoche,有一个“技巧”可以让获取选择处理长选项:使用-作为选项规范,并在选项规范中使用前导冒号来消除错误报告。然而,他们的解决方案需要重复代码来处理短选项和长选项。

这是我测试过的解决方案,它满足我的所有要求,并且不会重复处理短选项和长选项。为了清晰和完整,我已经改变了用户名并添加了切换演示不取值的选项。

解析器

#!/bin/bash

Options=("$@")
while getopts ":s:-:" OptChar "${Options[@]}"; do
   case "$OptChar" in
    -)   case "$OPTARG" in
         set|set=*)
            if [[ $OPTARG =~ ^set= ]] ; then
               Value="${Options[$OPTIND-2]#*=}"
            else
               Value="${Options[$OPTIND-1]}"
               ((OPTIND++))
            fi
            echo "Parsed: --$OPTARG, value: '$Value'"
            ;;

         toggle)
            echo "Parsed: --$OPTARG";;
         *) ((OPTIND--)); break;;
         esac
         ;;

   # Redirect short arguments to long arguments
   s)    ((OPTIND-=2)); Options[OPTIND-1]="--set";;
   t)    ((OPTIND--)); Options[OPTIND-1]="--toggle";;

   *)    ((OPTIND--)); break;;
   esac
done
Options=( "${Options[@]:$OPTIND-1}" )

echo "REMAINING Options: ${Options[@]}"

这是我的测试代码:

% ./parse.sh --set=a  -s b --set= --set c --unknown -s d --set e
Parsed: --set=a, value: 'a'
Parsed: --set, value: 'b'
Parsed: --set=, value: ''
Parsed: --set, value: 'c'
REMAINING Options: --unknown -s d --set e
%
% ./parse.sh -s a -x -s b
Parsed: --set, value: 'a'
REMAINING Options: -x -s b
%

答案2

如果您不使用“-ax”等组合短选项,这可能会起作用:

OPTIONS=("-u" "name1" "--username=name2" "-x" "a" -u "b" "c")

for i in `seq ${#OPTIONS[@]} -1 0`; do
  OFIRST=("${OPTIONS[@]:0:$i}")
  getopt -o u: -l username: -n programname -- "${OFIRST[@]}" &>/dev/null && break
done

NEWOPT=("${OPTIONS[@]:0:$i}" "--"  "${OPTIONS[@]:$i}")

getopt -o u: -l username: -n programname -- "${NEWOPT[@]}"

我确信这个解决方案存在问题。也许您最好尝试将此功能添加到 util-linux 的 getopt.c 源代码中。

相关内容