我希望使用我的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时必须进行自己的错误检查。
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[=]]" >&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检查和选项参数分析。 上面的脚本片段显示了这可以如何手动完成。 基本原理也适用于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