在bash shell脚本中使用getopts来获取长命令和短命令行选项
我希望使用我的shell脚本调用多种形式的命令行选项。
我知道可以使用getopts
,但就像在Perl中一样,我还没有能够对shell执行相同的操作。
关于如何完成这些任何想法,以便我可以使用如下选项:
./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/
在上面,这两个命令对我的shell来说意味着相同的东西,但是使用getopts
,我还没有能够实现这些?
bash getopts
builtin不支持带有双点划线前缀的长选项名称。 它只支持单字符选项。
有一个shell工具getopt
是另一个程序,而不是bash内建的。 getopt(3)
的GNU实现(由Linux上的命令行getopt(1)
使用)支持解析长选项。
但是getopt
的BSD实现(例如在Mac OS X上)不支持长选项。
其他一些答案显示了使用bash内建getopts
来模拟长选项的解决方案。 这个解决方案实际上是一个简短的选项,其特征是“ - ”。 所以你得到“ - ”作为国旗。 接下来的任何事情都会变成OPTARG,然后用嵌套的case
测试OPTARG。
这很聪明,但它带有警告:
getopts
无法执行opt规范。 如果用户提供无效选项,它不会返回错误。 您在解析OPTARG时必须进行自己的错误检查。 因此尽管可以编写更多的代码来解决长期选项缺乏支持的问题,但这还有很多工作要做,并且部分失败了使用getopt分析器来简化代码的目的。
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
支持长名称的命令行选项。(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
,具有不同的功能和不同的调用约定getopt
基本的getopt
很破碎:它不仅处理长选项,而且甚至无法处理嵌入空格或空参数中的空格,而getopts
确实是这样做的。 上面的代码在基本的getopt
不起作用。 GNU getopt
默认安装在Linux上,但在Mac OS X和FreeBSD上需要单独安装。 在Mac OS X上,安装MacPorts(http://www.macports.org),然后执行sudo port install getopt
以安装GNU getopt
(通常getopt
/opt/local/bin
),并确保/opt/local/bin
位于/usr/bin
之前的shell路径中。 在FreeBSD上,安装misc/getopt
。
修改您自己程序的示例代码的快速指南:在开头的几行中,除了调用getopt
那行外,所有代码都应该保持不变。 您应该在-n
之后更改程序名称,在-o
之后指定短的选项,以及--long
之后的长选项。 将冒号放在具有价值的选项之后。
最后,如果你看到刚刚set
代码而不是eval set
,那么它就是为BSD getopt
编写的。 您应该将其更改为使用eval set
样式,这对于两个版本的getopt
都可以正常工作,而普通set
不适用于GNU getopt
。
1Actually, getopts
在ksh93
支持长命名的选项,但这个壳不作为经常bash
。 在zsh
,使用zparseopts
来获得这个功能。
2在技术上,“GNU getopt
”是一个误用词; 这个版本实际上是为Linux而不是GNU项目编写的。 但是,它遵循所有GNU约定,并且术语“GNU getopt
”通常被使用(例如在FreeBSD上)。
Bash内建的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[=]<value>]" >&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[=]<value>]
$ ./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
检查和选项参数分析。 上面的脚本片段显示了这可以如何手动完成。 基本原理也适用于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指出的那样,这个技巧利用了允许选项参数(即“-f filename”中的文件名)的shell的非标准行为。连接到选项(如“-ffilename”)。 POSIX标准说明它们之间必须有一个空格,在“ - longoption”的情况下将终止选项解析并将所有longoption变为非选项参数。
链接地址: http://www.djcxy.com/p/97097.html上一篇: Using getopts in bash shell script to get long and short command line options