在 shell 脚本中,处理像 -a -b 这样的选项但保留 --abc 吗?

在 shell 脚本中,处理像 -a -b 这样的选项但保留 --abc 吗?

所以我有一个 shell 脚本,它做了一些准备,然后运行一个实用程序。

准备工作可能会受到-a或等开关的影响-n。不是此类开关的命令行内容将传递给实用程序。目前我的脚本有:

while getopts ":an" opt; do
  case $opt in
    a)
      #something
      ;;
    n)
      #something else
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      ;;
  esac
done
shift $((OPTIND-1))
  #more prep
my_utility $@

但是,当我想将长格式选项传递给实用程序时,这会失败,例如getopts--abc解释为ab和选项。c

那么我怎样才能以这样的方式处理选项,-a要么-n被处理,但--abc保持不变?当然,除非我转储 Shell 并在 Python 中执行 - 那么选项将非常简单,但我还需要复制文件并运行实用程序,而 Python 使这变得很尴尬。

答案1

要解释双破折号命令,您需要 GNUgetopt而不是内置的getopts.

有一种方法可以使用内置的 bashgetopts来模拟长选项,但您不应该这样做,因为这是一个拼凑,不值得您付出努力,因为getopts不能执行长规格。

所以你的脚本变成:

#!/bin/bash

# Get options
OPTS=`getopt --options an: --long a_something,n_something_else: -n 'parse-options' -- "$@"`

if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi

#echo parsed and cleaned up options
echo "$OPTS"
eval set -- "$OPTS"

bSomething=false
bSomethingElse=false

# "Endless" loop over the parsed options
while true; do
  case "$1" in
    -a | --a_something )       bSomething=true;     shift ;;
    -n | --n_something_else )  bSomethingElse=true; shift ;;
    -- )                                            shift; break ;;
    * )                                                    break ;;
  esac
done

了解更多信息:man getopt
欲了解更多信息:man getopt(3)

答案2

我不明白为什么你不能通过将-其视为需要参数的短选项来模拟长选项:

$ cat /tmp/foo
while getopts :aq:-: opt; do
        case $opt in
        a)      echo option -a;;
        q)      echo option -q with value "$OPTARG";;
        -)      case $OPTARG in
                abc)    echo option --abc;;
                def=*)  echo option --def with value "${OPTARG#*=}";;
                *)      echo >&2 "unknown option --$OPTARG";;
                esac;;
        :)      echo >&2 "-$OPTARG needs an argument";;
        *)      echo >&2 "unknown option -$OPTARG";;
        esac
done
shift "$((OPTIND - 1))"
echo argv: "$@"

$ sh /tmp/foo -a -qt --abc --def=ghi -- foo bar
option -a
option -q with value t
option --abc
option --def with value ghi
argv: foo bar

如果你想创建一个参数列表(例如用它调用另一个命令),并且你的 shell 不支持数组(例如 debian 或 busybox /bin/sh),你可以使用以下技巧,它将传递给 eval 一个列表参数索引而不是实际字符串;这将避免任何 IFS 分割/通配/空格烦恼:

$ cat /tmp/foo
# handle -a and -qval
# call another command with any long options and non-option arguments
av=
while getopts :aq:-: opt; do
        case $opt in
        a)      echo option -a;;
        q)      echo option -q with value "'$OPTARG'";;
        -)      av="$av \"\${$((OPTIND-1))}\"" ;; # pass long options unchanged
        :)      echo >&2 "-$OPTARG needs an argument";;
        *)      echo >&2 "unknown option -$OPTARG";;
        esac
done
i=$OPTIND; while [ "$i" -le "$#" ]; do av="$av \"\${$i}\"" i=$((i + 1)); done

print_its_args(){ for a; do printf ' {%s}' "$a"; done; echo; }
echo "print_its_args $av"
eval "print_its_args $av"

然后:

$ sh /tmp/foo -aqval --foo='a  **  b' --abc -- '(moo)'
option -a
option -q with value 'val'
print_its_args  "$2" "$3" "$5"
 {--foo=a  **  b} {--abc} {(moo)}

这个技巧可以用在其他地方情况;但在这种情况下,不能使用像set -- "$@" arg将 args 推入这样的更简单的解决方案,因为$@修改 $@循环内部getopts不能以可移植的方式完成。

相关内容