当前位置:  开发笔记 > 编程语言 > 正文

在bash shell脚本中使用getopts来获取长和短的命令行选项

如何解决《在bashshell脚本中使用getopts来获取长和短的命令行选项》经验,为你挑选了16个好方法。

我希望使用我的shell脚本调用多种形式的命令行选项.

我知道getopts可以使用,但就像在Perl中一样,我无法对shell做同样的事情.

关于如何做到这一点的任何想法,以便我可以使用如下选项:

./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/

在上面,两个命令对我的shell意味着相同的东西,但使用getopts,我还没有能够实现这些?



1> Urban Vagabo..:

getopt并且getopts是不同的野兽,人们似乎对他们所做的事情有一些误解. getopts是一个内置命令,bash用于处理循环中的命令行选项,并将每个找到的选项和值依次分配给内置变量,以便您可以进一步处理它们. getopt但是,它是一个外部实用程序,它实际上并不像 bash getopts,Perl Getopt模块或Python optparse/ argparse模块那样为您处理选项.所有这一切getopt确实是规范化所传递的选项-即它们转换为更规范的形式,因此,它是一个shell脚本来处理它们更容易.例如,应用程序getopt可能会转换以下内容:

myscript -ab infile.txt -ooutfile.txt

进入这个:

myscript -a -b -o outfile.txt infile.txt

你必须自己做实际的处理.getopt如果对指定选项的方式进行各种限制,则根本不必使用:

每个参数只放一个选项;

所有选项都在任何位置参数之前(即非选项参数);

对于具有值的选项(例如,-o上面),该值必须作为单独的参数(在空格之后).

为什么用getopt而不是getopts?基本原因是只有GNU getopt才能支持长命名的命令行选项.1(GNU getopt是Linux上的默认设置.Mac OS X和FreeBSD有一个基本的,非常有用getopt,但可以安装GNU版本;见下文.)

例如,这是一个使用GNU的例子getopt,从我的脚本调用javawrap:

# NOTE: This requires GNU getopt.  On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \
             -n 'javawrap' -- "$@"`

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

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
  case "$1" in
    -v | --verbose ) VERBOSE=true; shift ;;
    -d | --debug ) DEBUG=true; shift ;;
    -m | --memory ) MEMORY="$2"; shift 2 ;;
    --debugfile ) DEBUGFILE="$2"; shift 2 ;;
    --minheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
    --maxheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

这允许您指定类似--verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"或类似的选项.调用的效果getopt是规范化选项,--verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"以便您可以更轻松地处理它们.引用"$1"并且"$2"很重要,因为它确保正确处理带有空格的参数.

如果删除前9行(通过该eval set行的所有内容),代码仍然有效!但是,您的代码在它接受的各种选项中会更加挑剔:特别是,您必须以上述"规范"形式指定所有选项.getopt但是,通过使用,您可以对单字母选项进行分组,使用较短的非模糊形式的长选项,使用--file foo.txt--file=foo.txt样式,使用-m 4096-m4096样式,混合选项和任何顺序的非选项等. getopt如果找到无法识别或不明确的选项,也会输出错误消息.

注意:实际上有两个完全不同的版本getopt,基本getopt和GNU getopt,具有不同的功能和不同的调用约定.2 Basic getopt非常破碎:它不仅不处理长选项,它甚至不能处理参数或空参数内的嵌入空格,而是getopts这样做.上面的代码在基本代码中不起作用getopt.GNU getopt默认安装在Linux上,但在Mac OS X和FreeBSD上需要单独安装.在Mac OS X上,安装MacPorts(http://www.macports.org),然后sudo port install getopt安装GNU getopt(通常是/opt/local/bin),并确保它/opt/local/bin在你的shell路径之前/usr/bin.在FreeBSD上,安装misc/getopt.

修改自己程序的示例代码的快速指南:在前几行中,除了调用的行之外,所有都是"样板文件"应该保持不变getopt.您应该在之后更改程序名称,在之后-n指定短选项,在之后指定-o长选项--long.在带有值的选项后放置冒号.

最后,如果您看到set代替的代码eval set,那么它就是为BSD编写的getopt.您应该将其更改为使用eval set样式,该样式适用于两个版本getopt,而普通版本set无法正常使用GNU getopt.

1实际上,getoptsksh93支持长命名的选项中,但是这个shell并不经常使用bash.在zsh,用于zparseopts获得此功能.

2从技术上讲,"GNU getopt"是用词不当; 这个版本实际上是为Linux而不是GNU项目编写的.但是,它遵循所有GNU约定,并且getopt通常使用术语"GNU "(例如在FreeBSD上).


这非常有用,使用getopt检查选项然后在一个非常简单的循环中处理这些选项的想法在我想要将长样式选项添加到bash脚本时非常有效.谢谢.
Linux上的`getopt`不是**GNU实用程序,传统的`getopt`最初不是来自BSD,而是来自AT&T Unix.ksh93的`getopts`(也来自AT&T)支持GNU风格的长选项.

2> Bill Karwin..:

可以考虑三种实现:

Bash内置getopts.这不支持带有双短划线前缀的长选项名称.它仅支持单字符选项.

BSD UNIX实现独立getopt命令(这是MacOS使用的).这也不支持长选项.

独立的GNU实现getopt.GNU getopt(3)(getopt(1)Linux上的命令行使用)支持解析长选项.


其他一些答案显示了使用bash内置getopts模拟长选项的解决方案.该解决方案实际上是一个短的选项,其字符是" - ".所以你得到" - "作为旗帜.然后,随后的任何内容都将成为OPTARG,并使用嵌套测试OPTARG case.

这很聪明,但它带有警告:

getopts无法强制执行opt规范.如果用户提供无效选项,则无法返回错误.在解析OPTARG时,您必须自己进行错误检查.

OPTARG用于长选项名称,当您的长选项本身具有参数时,这会使用量变得复杂.您最终必须自己编写代码作为附加案例.

因此,尽管可以编写更多代码来解决缺乏对长选项的支持的问题,但这需要做更多的工作,并且部分地违背了使用getopt解析器来简化代码的目的.


所以.什么是跨平台,便携式解决方案?
@Bill Karwin:"内置的bash getopts不支持带有双短划线前缀的长选项名称." 但是可以使用getopts来支持长选项:请参阅下面的http://stackoverflow.com/a/7680682/915044.
GNU Getopt似乎是唯一的选择.在Mac上,从macports安装GNU getopt.在Windows上,我将使用Cygwin安装GNU getopt.
[显然](http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?getopts+1),ksh getopts*可以*处理长选项.

3> 小智..:

Bash builtin getopts函数可用于通过在optspec中放入一个破折号字符后跟冒号来解析长选项:

#!/usr/bin/env bash 
optspec=":hv-:"
while getopts "$optspec" optchar; do
    case "${optchar}" in
        -)
            case "${OPTARG}" in
                loglevel)
                    val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                    echo "Parsing option: '--${OPTARG}', value: '${val}'" >&2;
                    ;;
                loglevel=*)
                    val=${OPTARG#*=}
                    opt=${OPTARG%=$val}
                    echo "Parsing option: '--${opt}', value: '${val}'" >&2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h)
            echo "usage: $0 [-v] [--loglevel[=]]" >&2
            exit 2
            ;;
        v)
            echo "Parsing option: '-${optchar}'" >&2
            ;;
        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done

复制到当前工作目录getopts_test.sh中的可执行文件名= 后,可以生成类似的输出

$ ./getopts_test.sh
$ ./getopts_test.sh -f
Non-option argument: '-f'
$ ./getopts_test.sh -h
usage: code/getopts_test.sh [-v] [--loglevel[=]]
$ ./getopts_test.sh --help
$ ./getopts_test.sh -v
Parsing option: '-v'
$ ./getopts_test.sh --very-bad
$ ./getopts_test.sh --loglevel
Parsing option: '--loglevel', value: ''
$ ./getopts_test.sh --loglevel 11
Parsing option: '--loglevel', value: '11'
$ ./getopts_test.sh --loglevel=11
Parsing option: '--loglevel', value: '11'

显然,getopts既不OPTERR对long选项执行检查也不执行option-argument解析.上面的脚本片段显示了如何手动完成此操作.基本原理也适用于Debian Almquist shell("dash").请注意特殊情况:

getopts -- "-:"  ## without the option terminator "-- " bash complains about "-:"
getopts "-:"     ## this works in the Debian Almquist shell ("dash")

请注意,正如来自http://mywiki.wooledge.org/BashFAQ的 GreyCat所指出的,这个技巧利用了shell的非标准行为,允许使用option-argument(即"-f filename"中的文件名)连接到选项(如"-ffilename").该POSIX标准说必须有它们之间的空间,这在的情况下" - longoption"将终止选项的解析,并把所有longoptions成非选项参数.


因为我们在这里进入bash equilibrism:在算术表达式中允许使用Naked变量名,不需要`$`.`OPTIND = $(($ OPTIND + 1))`可以只是`OPTIND = $((OPTIND + 1))`.更有趣的是,您甚至可以在算术表达式中分配和增加变量,因此可以将其进一步缩写为`:$((++ OPTIND))`,或甚至`((++ OPTIND))`考虑到`++ OPTIND`将始终为正,因此它不会使用`-e`选项绊倒shell运行.:-) http://www.gnu.org/software/bash/manual/html_node/Shell-Arithmetic.html
一个问题:val =“ $ {!OPTIND}`中`!`的语义是什么?
@TomRoche,它是间接替换:http://unix.stackexchange.com/a/41293/84316
@ecbrodie:这是因为实际上已经处理了两个参数,而不是一个.第一个参数是单词"loglevel",下一个是*that*参数的参数.同时,`getopts`自动只增加'OPTIND`为1,但在我们的例子中我们需要它增加2,所以我们手动增加1,然后让`getopts`再次为我们增加1.
为什么“-非常糟糕”不发出警告?

4> Jonathan Lef..:

内置getopts命令仍为AFAIK,仅限于单字符选项.

有(或曾经是)一个外部程序getopt,它会重新组织一组选项,以便更容易解析.您可以调整该设计以处理长选项.用法示例:

aflag=no
bflag=no
flist=""
set -- $(getopt abf: "$@")
while [ $# -gt 0 ]
do
    case "$1" in
    (-a) aflag=yes;;
    (-b) bflag=yes;;
    (-f) flist="$flist $2"; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*)  break;;
    esac
    shift
done

# Process remaining non-option arguments
...

您可以使用与getoptlong命令类似的方案.

请注意,外部getopt程序的根本弱点是难以处理带有空格的参数,并且难以准确地保留这些空间.这就是内置getopts优越的原因,尽管它只处理单字母选项.


除了GNU版本(具有不同的调用约定)之外,getopt从根本上被打破了.不要使用它.请使用**getopts而不是http://bash-hackers.org/wiki/doku.php/howto/getopts_tutorial
@hendry - 来自你自己的链接:"请注意,getopts无法解析GNU风格的长选项( - myoption)或XF86风格的长选项(-myoption)!"

5> sme..:

这是一个实际使用带有长选项的getopt的示例:

aflag=no
bflag=no
cargument=none

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc: -l along,blong,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag="yes" ;;
    -b|--blong) bflag="yes" ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done



6> Adam Katz..:

可以通过标准getopts内置解析长选项作为-"选项"的"参数"

这是可移植的本机POSIX shell - 不需要外部程序或基本原理.

本指南执行长选项作为参数传递给-选项,因此--alpha被看作getopts-与参数alpha--bravo=foo被视为-与参数bravo=foo.真正的论点可以用简单的替代品来收获:${OPTARG#*=}.

在此示例中,-b(及其长形式--bravo)具有强制选项(请注意对长格式强制执行的手动重建).长参数的非布尔选项在等号之后出现,例如--bravo=foo(长选项的空格分隔符很难实现).

因为这样使用getopts,这个解决方案支持使用类似cmd -ac --bravo=foo -d FILE(它具有组合选项-a和 - c并将长选项与标准选项交错),而大多数其他答案在这里要么努力要么不能做到.

while getopts ab:c-: arg; do
  case $arg in
    a )  ARG_A=true ;;
    b )  ARG_B="$OPTARG" ;;
    c )  ARG_C=true ;;
    - )  LONG_OPTARG="${OPTARG#*=}"
         case $OPTARG in
           alpha    )  ARG_A=true ;;
           bravo=?* )  ARG_B="$LONG_OPTARG" ;;
           bravo*   )  echo "No arg for --$OPTARG option" >&2; exit 2 ;;
           charlie  )  ARG_C=true ;;
           alpha* | charlie* )
                       echo "No arg allowed for --$OPTARG option" >&2; exit 2 ;;
           '' )        break ;; # "--" terminates argument processing
           * )         echo "Illegal option --$OPTARG" >&2; exit 2 ;;
         esac ;;
    \? ) exit 2 ;;  # getopts already reported the illegal option
  esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list

当参数是一个破折号(-)时,它还有两个组件:标志名称和(可选)其参数.我将这些标准方式划分为任何命令,第一个等号(=). $LONG_OPTARG因此,仅仅是$OPTARG没有国旗名称或等号的内容.

内部case手动实现长选项,因此需要一些内务处理:

bravo=?匹配--bravo=foo但不匹配--bravo=(注意:case第一场比赛后停止)

bravo*如下,并指出在缺少必需的参数--bravo--bravo=

alpha* | charlie* 捕获给不支持它们的选项的参数

'' 用于支持碰巧以破折号开头的非选项

* 捕获所有其他长选项并重新创建getopts为无效选项抛出的错误

您不一定需要所有这些家政用品.例如,您可能希望--bravo拥有一个可选参数(-b由于其中的限制而无法支持getopts).只需删除=?相关的故障情况,然后${ARG_B:=$DEFAULT_ARG_B}在第一次使用时拨打电话$ARG_B.



7> 小智..:

看看shFlags是一个可移植的shell库(意思是:Linux,Solaris等上的sh,bash,dash,ksh,zsh).

它使添加新标志就像在脚本中添加一行一样简单,它提供了一个自动生成的使用功能.

这是一个简单的Hello, world!使用shFlag:

#!/bin/sh

# source shflags from current directory
. ./shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# say hello
echo "Hello, ${FLAGS_name}!"

对于具有支持长选项的增强型getopt(例如Linux)的操作系统,您可以执行以下操作:

$ ./hello_world.sh --name Kate
Hello, Kate!

其余的,你必须使用短选项:

$ ./hello_world.sh -n Kate
Hello, Kate!

添加新标志就像添加新标志一样简单DEFINE_ call.


@UrbanVagabond –遗憾的是,非系统默认工具的安装对于具有足够便携性的工具来说不是可接受的要求。

8> 小智..:
使用getopts短/长选项和参数

适用于所有组合,例如:

foob​​ar -f --bar

foob​​ar --foo -b

foob​​ar -bf --bar --foobar

foob​​ar -fbFBAshorty --bar -FB --arguments = longhorn

foob​​ar -fA"text shorty"-B --arguments ="text longhorn"

bash foobar -F --barfoo

sh foobar -B --foobar - ...

bash ./foobar -F --bar


这个例子的一些声明

Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

Usage函数的外观如何

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                  -f   --foo            Set foo to yes    ($bart)
                  -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

getops 有长/短标志以及长参数

while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
             --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

产量

##################################################################
echo "----------------------------------------------------------"
echo "RESULT short-foo      : $sfoo                                    long-foo      : $lfoo"
echo "RESULT short-bar      : $sbar                                    long-bar      : $lbar"
echo "RESULT short-foobar   : $sfoobar                                 long-foobar   : $lfoobar"
echo "RESULT short-barfoo   : $sbarfoo                                 long-barfoo   : $lbarfoo"
echo "RESULT short-arguments: $sarguments  with Argument = \"$sARG\"   long-arguments: $larguments and $lARG"

将上述内容组合成一个有凝聚力的脚本
#!/bin/bash
# foobar: getopts with short and long options AND arguments

function _cleanup ()
{
  unset -f _usage _cleanup ; return 0
}

## Clear out nested functions on exit
trap _cleanup INT EXIT RETURN

###### some declarations for this example ######
Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                    -f   --foo            Set foo to yes    ($bart)
                      -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

##################################################################    
#######  "getopts" with: short options  AND  long options  #######
#######            AND  short/long arguments               #######
while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
               --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done



9> mtvee..:

其他方式...

# translate long options to short
for arg
do
    delim=""
    case "$arg" in
       --help) args="${args}-h ";;
       --verbose) args="${args}-v ";;
       --config) args="${args}-c ";;
       # pass through anything else
       *) [[ "${arg:0:1}" == "-" ]] || delim="\""
           args="${args}${delim}${arg}${delim} ";;
    esac
done
# reset the translated args
eval set -- $args
# now we can process with getopt
while getopts ":hvc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        c)  source $OPTARG ;;
        \?) usage ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage
        ;;
    esac
done



10> 小智..:

我有点解决这个问题:

# A string with command options
options=$@

# An array with all the arguments
arguments=($options)

# Loop index
index=0

for argument in $options
  do
    # Incrementing index
    index=`expr $index + 1`

    # The conditions
    case $argument in
      -a) echo "key $argument value ${arguments[index]}" ;;
      -abc) echo "key $argument value ${arguments[index]}" ;;
    esac
  done

exit;

我愚蠢还是什么?getopt而且getopts很混乱.


你根本不是傻瓜,但是你可能缺少一个功能:getopt(s)可以识别混合的选项(例如:`-ltr`或`-lt -r`以及`-l -t -r` ).它还提供了一些错误处理,并且一旦选项处理完成,就可以轻松地将处理过的参数移开.
绝对是我见过的最简单的方式.我改变了一点,比如使用i = $(($ i + 1))而不是expr,但概念是气密的.

11> 小智..:

如果您不想要getopt依赖项,可以这样做:

while test $# -gt 0
do
  case $1 in

  # Normal option processing
    -h | --help)
      # usage and help
      ;;
    -v | --version)
      # version info
      ;;
  # ...

  # Special cases
    --)
      break
      ;;
    --*)
      # error unknown (long) option $1
      ;;
    -?)
      # error unknown (short) option $1
      ;;

  # FUN STUFF HERE:
  # Split apart combined short options
    -*)
      split=$1
      shift
      set -- $(echo "$split" | cut -c 2- | sed 's/./-& /g') "$@"
      continue
      ;;

  # Done with options
    *)
      break
      ;;
  esac

  # for testing purposes:
  echo "$1"

  shift
done

当然,那么你不能在一个短划线上使用长样式选项.如果你想添加缩短的版本(例如--verbos而不是--verbose),那么你需要手动添加它们.

但是,如果您希望获得getopts功能以及长选项,这是一种简单的方法.

我还把这个片段放在一个要点上.



12> Nietzche-jou..:

内置getopts无法做到这一点.有一个外部的getopt(1)程序可以做到这一点,但你只能在Linux上从util-linux包中获得它.它附带一个示例脚本getopt-parse.bash.

还有一个getopts_long写为shell函数.


(1)程序可以做到这一点,但你只能在Linux上从
包中获得它.它附带一个示例脚本
.
`getopt`于1993年包含在FreeBSD 1.0版中,从那时起一直是FreeBSD的一部分.因此,它被从FreeBSD 4.x中采用,包含在Apple的Darwin项目中.从OS X 10.6.8开始,Apple包含的手册页仍然与FreeBSD手册页完全相同.所以,是的,它包含在OS X和Linux之外的其他操作系统的gobs中.-1在这个错误信息的答案.

13> 3ED..:
#!/bin/bash
while getopts "abc:d:" flag
do
  case $flag in
    a) echo "[getopts:$OPTIND]==> -$flag";;
    b) echo "[getopts:$OPTIND]==> -$flag";;
    c) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
    d) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
  esac
done

shift $((OPTIND-1))
echo "[otheropts]==> $@"

exit

.

#!/bin/bash
until [ -z "$1" ]; do
  case $1 in
    "--dlong")
      shift
      if [ "${1:1:0}" != "-" ]
      then
        echo "==> dlong $1"
        shift
      fi;;
    *) echo "==> other $1"; shift;;
  esac
done
exit



14> 小智..:

ksh93,getopts支持长名称...

while getopts "f(file):s(server):" flag
do
    echo "$flag" $OPTIND $OPTARG
done

或者我发现的教程已经说过了.试试看吧.


这是内置的ksh93的getopts.除了这种语法,它还有一个更复杂的语法,也允许长选项,没有短的等价,等等.
一个合理的答案。OP未指定WHAT Shell。

15> phils..:

发明轮子的另一个版本......

这个函数是(希望)POSIX兼容的普通bourne shell替代GNU getopt.它支持短/长选项,可以接受强制/可选/无参数,并且指定选项的方式几乎与GNU getopt相同,因此转换是微不足道的.

当然,这仍然是放入脚本的相当大的代码块,但它大约是众所周知的getopt_long shell函数的一半,并且在您只想替换现有GNU getopt用途的情况下可能更好.

这是相当新的代码,所以YMMV(绝对请让我知道如果这实际上不是以任何理由POSIX兼容 - 便携性从一开始的打算,但我没有一个有用的POSIX测试环境).

代码和示例用法如下:

#!/bin/sh
# posix_getopt shell function
# Author: Phil S.
# Version: 1.0
# Created: 2016-07-05
# URL: http://stackoverflow.com/a/37087374/324105

# POSIX-compatible argument quoting and parameter save/restore
# http://www.etalabs.net/sh_tricks.html
# Usage:
# parameters=$(save "$@") # save the original parameters.
# eval "set -- ${parameters}" # restore the saved parameters.
save () {
    local param
    for param; do
        printf %s\\n "$param" \
            | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
    done
    printf %s\\n " "
}

# Exit with status $1 after displaying error message $2.
exiterr () {
    printf %s\\n "$2" >&2
    exit $1
}

# POSIX-compatible command line option parsing.
# This function supports long options and optional arguments, and is
# a (largely-compatible) drop-in replacement for GNU getopt.
#
# Instead of:
# opts=$(getopt -o "$shortopts" -l "$longopts" -- "$@")
# eval set -- ${opts}
#
# We instead use:
# opts=$(posix_getopt "$shortopts" "$longopts" "$@")
# eval "set -- ${opts}"
posix_getopt () { # args: "$shortopts" "$longopts" "$@"
    local shortopts longopts \
          arg argtype getopt nonopt opt optchar optword suffix

    shortopts="$1"
    longopts="$2"
    shift 2

    getopt=
    nonopt=
    while [ $# -gt 0 ]; do
        opt=
        arg=
        argtype=
        case "$1" in
            # '--' means don't parse the remaining options
            ( -- ) {
                getopt="${getopt}$(save "$@")"
                shift $#
                break
            };;
            # process short option
            ( -[!-]* ) {         # -x[foo]
                suffix=${1#-?}   # foo
                opt=${1%$suffix} # -x
                optchar=${opt#-} # x
                case "${shortopts}" in
                    ( *${optchar}::* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *${optchar}:* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 "$1 requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 "$1 requires an argument";;
                            esac
                        fi
                    };;
                    ( *${optchar}* ) { # no argument
                        argtype=none
                        arg=
                        shift
                        # Handle multiple no-argument parameters combined as
                        # -xyz instead of -x -y -z. If we have just shifted
                        # parameter -xyz, we now replace it with -yz (which
                        # will be processed in the next iteration).
                        if [ -n "${suffix}" ]; then
                            eval "set -- $(save "-${suffix}")$(save "$@")"
                        fi
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # process long option
            ( --?* ) {            # --xarg[=foo]
                suffix=${1#*=}    # foo (unless there was no =)
                if [ "${suffix}" = "$1" ]; then
                    suffix=
                fi
                opt=${1%=$suffix} # --xarg
                optword=${opt#--} # xarg
                case ",${longopts}," in
                    ( *,${optword}::,* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *,${optword}:,* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 \
                                       "--${optword} requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 \
                                       "--${optword} requires an argument";;
                            esac
                        fi
                    };;
                    ( *,${optword},* ) { # no argument
                        if [ -n "${suffix}" ]; then
                            exiterr 1 "--${optword} does not take an argument"
                        fi
                        argtype=none
                        arg=
                        shift
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # any other parameters starting with -
            ( -* ) exiterr 1 "Unknown option $1";;
            # remember non-option parameters
            ( * ) nonopt="${nonopt}$(save "$1")"; shift;;
        esac

        if [ -n "${opt}" ]; then
            getopt="${getopt}$(save "$opt")"
            case "${argtype}" in
                ( optional|required ) {
                    getopt="${getopt}$(save "$arg")"
                };;
            esac
        fi
    done

    # Generate function output, suitable for:
    # eval "set -- $(posix_getopt ...)"
    printf %s "${getopt}"
    if [ -n "${nonopt}" ]; then
        printf %s "$(save "--")${nonopt}"
    fi
}

用法示例:

# Process command line options
shortopts="hvd:c::s::L:D"
longopts="help,version,directory:,client::,server::,load:,delete"
#opts=$(getopt -o "$shortopts" -l "$longopts" -n "$(basename $0)" -- "$@")
opts=$(posix_getopt "$shortopts" "$longopts" "$@")
if [ $? -eq 0 ]; then
    #eval set -- ${opts}
    eval "set -- ${opts}"
    while [ $# -gt 0 ]; do
        case "$1" in
            ( --                ) shift; break;;
            ( -h|--help         ) help=1; shift; break;;
            ( -v|--version      ) version_help=1; shift; break;;
            ( -d|--directory    ) dir=$2; shift 2;;
            ( -c|--client       ) useclient=1; client=$2; shift 2;;
            ( -s|--server       ) startserver=1; server_name=$2; shift 2;;
            ( -L|--load         ) load=$2; shift 2;;
            ( -D|--delete       ) delete=1; shift;;
        esac
    done
else
    shorthelp=1 # getopt returned (and reported) an error.
fi



16> pauljohn32..:

我不时只写shell脚本,但并没有实践,因此,感谢您提供任何反馈。

使用@Arvid Requate提出的策略,我们注意到了一些用户错误。忘记包含值的用户将不小心将下一个选项的名称视为值:

./getopts_test.sh --loglevel= --toc=TRUE

将导致“ loglevel”的值被视为“ --toc = TRUE”。这是可以避免的。

我从http://mwiki.wooledge.org/BashFAQ/035关于手动解析的讨论中采纳了一些有关检查CLI的用户错误的想法 。我将错误检查插入到处理“-”和“-”参数中。

然后,我开始摆弄语法,所以这里的任何错误都是我的错,而不是原始作者。

我的方法可以帮助希望长时间输入带有或不带有等号的用户。也就是说,它对“ --loglevel 9”的响应应该与“ --loglevel = 9”相同。在-/ space方法中,无法确定用户是否忘记了参数,因此需要进行一些猜测。

    如果用户使用长/等号格式(--opt =),则=后面的空格会触发错误,因为未提供参数。

    如果用户具有长/空格参数(--opt),则该脚本将导致失败,如果没有参数跟随(命令末尾)或参数以破折号开头)

如果您刚开始,“-opt = value”和“ --opt value”格式之间会有一个有趣的区别。带有等号的命令行参数被视为“ opt = value”,而要处理的工作是字符串解析,以“ =”分隔。相反,使用“ --opt值”时,参数的名称为“ opt”,因此我们面临着在命令行中获取下一个值的挑战。这就是@Arvid Requate使用$ {!OPTIND}(间接引用)的地方。我还是完全不了解,BashFAQ中的评论似乎警告了这种风格(http://mywiki.wooledge.org/BashFAQ/006)。顺便说一句,我不认为以前的海报有关OPTIND = $((($ OPTIND + 1))的重要性的评论是正确的。我是说

在此脚本的最新版本中,标志-v表示VERBOSE打印输出。

将其保存在名为“ cli-5.sh”的文件中,使其成为可执行文件,这些文件中的任何一个都将起作用,或者以所需的方式失败

./cli-5.sh  -v --loglevel=44 --toc  TRUE
./cli-5.sh  -v --loglevel=44 --toc=TRUE
./cli-5.sh --loglevel 7
./cli-5.sh --loglevel=8
./cli-5.sh -l9

./cli-5.sh  --toc FALSE --loglevel=77
./cli-5.sh  --toc=FALSE --loglevel=77

./cli-5.sh   -l99 -t yyy
./cli-5.sh   -l 99 -t yyy

这是对intpu用户进行错误检查的示例输出

$ ./cli-5.sh  --toc --loglevel=77
ERROR: toc value must not have dash at beginning
$ ./cli-5.sh  --toc= --loglevel=77
ERROR: value for toc undefined

您应该考虑打开-v,因为它会打印出OPTIND和OPTARG的内部信息

#/usr/bin/env bash

## Paul Johnson
## 20171016
##

## Combines ideas from
## /sf/ask/17360801/
## by @Arvid Requate, and http://mwiki.wooledge.org/BashFAQ/035

# What I don't understand yet: 
# In @Arvid REquate's answer, we have 
# val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
# this works, but I don't understand it!


die() {
    printf '%s\n' "$1" >&2
    exit 1
}

printparse(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
    fi
}

showme(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'VERBOSE: %s\n' "$1" >&2;
    fi
}


VERBOSE=0
loglevel=0
toc="TRUE"

optspec=":vhl:t:-:"
while getopts "$optspec" OPTCHAR; do

    showme "OPTARG:  ${OPTARG[*]}"
    showme "OPTIND:  ${OPTIND[*]}"
    case "${OPTCHAR}" in
        -)
            case "${OPTARG}" in
                loglevel) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) # CAUTION! no effect?
                    printparse "--${OPTARG}" "  " "${val}"
                    loglevel="${val}"
                    shift
                    ;;
                loglevel=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        printparse "--${opt}" "=" "${val}"
                        loglevel="${val}"
                        ## shift CAUTION don't shift this, fails othewise
                    else
                        die "ERROR: $opt value must be supplied"
                    fi
                    ;;
                toc) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) #??
                    printparse "--${opt}" " " "${val}"
                    toc="${val}"
                    shift
                    ;;
                toc=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        toc=${val}
                        printparse "--$opt" " -> " "$toc"
                        ##shift ## NO! dont shift this
                    else
                        die "ERROR: value for $opt undefined"
                    fi
                    ;;

                help)
                    echo "usage: $0 [-v] [--loglevel[=]] [--toc[=]]" >&2
                    exit 2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h|-\?|--help)
            ## must rewrite this for all of the arguments
            echo "usage: $0 [-v] [--loglevel[=]] [--toc[=]]" >&2
            exit 2
            ;;
        l)
            loglevel=${OPTARG}
            printparse "-l" " "  "${loglevel}"
            ;;
        t)
            toc=${OPTARG}
            ;;
        v)
            VERBOSE=1
            ;;

        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done



echo "
After Parsing values
"
echo "loglevel  $loglevel" 
echo "toc  $toc"

推荐阅读
手机用户2502851955
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有