我希望使用shell脚本调用长形式和短形式的命令行选项。
我知道可以使用getopts,但是像在Perl中一样,我不能用shell做同样的事情。
关于如何做到这一点的任何想法,这样我就可以使用如下选项:
./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/
在上面,这两个命令对我的shell意味着同样的事情,但使用getopts,我还不能实现这些?
我希望使用shell脚本调用长形式和短形式的命令行选项。
我知道可以使用getopts,但是像在Perl中一样,我不能用shell做同样的事情。
关于如何做到这一点的任何想法,这样我就可以使用如下选项:
./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/
在上面,这两个命令对我的shell意味着同样的事情,但使用getopts,我还不能实现这些?
当前回答
看一下shFlags,它是一个可移植的shell库(意思是:sh, bash, dash, ksh, zsh在Linux, Solaris等上)。
它使得添加新标志就像在脚本中添加一行一样简单,并且它提供了一个自动生成的用法函数。
这是一个简单的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_调用一样简单。
其他回答
下面你可以找到bash中复杂选项解析的几种不同方法: http://mywiki.wooledge.org/ComplexOptionParsing
我确实创建了下面的一个,而且我认为它很好,因为它的代码最少 多头和空头选择都有效。使用这种方法,长选项也可以有多个参数。
#!/bin/bash
# Uses bash extensions. Not portable as written.
declare -A longoptspec
longoptspec=( [loglevel]=1 ) #use associative array to declare how many arguments a long option expects, in this case we declare that loglevel expects/has one argument, long options that aren't listed i n this way will have zero arguments by default
optspec=":h-:"
while getopts "$optspec" opt; do
while true; do
case "${opt}" in
-) #OPTARG is name-of-long-option or name-of-long-option=value
if [[ "${OPTARG}" =~ .*=.* ]] #with this --key=value format only one argument is possible
then
opt=${OPTARG/=*/}
OPTARG=${OPTARG#*=}
((OPTIND--))
else #with this --key value1 value2 format multiple arguments are possible
opt="$OPTARG"
OPTARG=(${@:OPTIND:$((longoptspec[$opt]))})
fi
((OPTIND+=longoptspec[$opt]))
continue #now that opt/OPTARG are set we can process them as if getopts would've given us long options
;;
loglevel)
loglevel=$OPTARG
;;
h|help)
echo "usage: $0 [--loglevel[=]<value>]" >&2
exit 2
;;
esac
break; done
done
# End of file
另一种方式…
# 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
hm.
对纯bash选项不太满意。为什么不使用perl来得到你想要的。直接解析$*数组,并自动命名您的选项。
简单的帮助脚本:
#!/usr/bin/perl
use Getopt::Long;
my $optstring = shift;
my @opts = split(m#,#, $optstring);
my %opt;
GetOptions(\%opt, @opts);
print "set -- " . join(' ', map("'$_'", @ARGV)) . ";";
my $xx;
my $key;
foreach $key (keys(%opt))
{
print "export $key='$opt{$key}'; ";
}
然后你可以在你的脚本中使用一行,例如:
#!/bin/bash
eval `getopts.pl reuse:s,long_opt:s,hello $*`;
echo "HELLO: $hello"
echo "LONG_OPT: $long_opt"
echo "REUSE: $reuse"
echo $*
/tmp/script.sh hello——reuse me——long_opt whatever_you_want_except_spaces——hello 1 2 3
你好:1 LONG_OPT: whatever_you_want_except空格 重用:我
1, 2, 3
这里唯一需要注意的是空格不适用。但它避免了bash相当复杂的循环语法,适用于长参数,自动将它们命名为变量并自动调整$*的大小,因此99%的时间都是有效的。
可以考虑以下三种实现方式:
Bash内置的getopts。这不支持带有双破折号前缀的长选项名。它只支持单字符选项。 BSD UNIX实现的独立getopt命令(这是MacOS使用的)。这也不支持长选项。 独立getopt的GNU实现。GNU getopt(3)(由Linux上的命令行getopt(1)使用)支持解析长选项。
其他一些答案给出了使用bash内置getopts模拟长选项的解决方案。该解决方案实际上生成了一个字符为“-”的短选项。所以你得到“——”作为标志。然后,后面的所有内容都变成OPTARG,并使用嵌套的case测试OPTARG。
这很聪明,但也需要注意:
getopts不能强制执行opt规范。如果用户提供了无效的选项,它不能返回错误。在解析OPTARG时,您必须自己进行错误检查。 OPTARG用于长选项名称,当长选项本身有参数时,这会使使用复杂化。你最终不得不自己编写代码作为一个额外的案例。
因此,虽然可以编写更多的代码来解决长选项支持不足的问题,但工作量要大得多,并且在一定程度上违背了使用getopt解析器来简化代码的目的。
对于getopts部分,如果需要较长的命令行选项,使用ksh可能更简单,因为在ksh中更容易完成。
# Working Getopts Long => KSH
#! /bin/ksh
# Getopts Long
USAGE="s(showconfig)"
USAGE+="c:(createdb)"
USAGE+="l:(createlistener)"
USAGE+="g:(generatescripts)"
USAGE+="r:(removedb)"
USAGE+="x:(removelistener)"
USAGE+="t:(createtemplate)"
USAGE+="h(help)"
while getopts "$USAGE" optchar ; do
case $optchar in
s) echo "Displaying Configuration" ;;
c) echo "Creating Database $OPTARG" ;;
l) echo "Creating Listener LISTENER_$OPTARG" ;;
g) echo "Generating Scripts for Database $OPTARG" ;;
r) echo "Removing Database $OPTARG" ;;
x) echo "Removing Listener LISTENER_$OPTARG" ;;
t) echo "Creating Database Template" ;;
h) echo "Help" ;;
esac
done