bash 是否可以将 getopts 与位置参数混合使用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11742996/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is mixing getopts with positional parameters possible?
提问by Bharat Sinha
I want to design a shell script as a wrapper for a couple of scripts. I would like to specify parameters for myshell.sh
using getopts
and pass the remaining parameters in the same order to the script specified.
我想设计一个 shell 脚本作为几个脚本的包装器。我想指定用于myshell.sh
使用getopts
的参数并将其余参数以相同的顺序传递给指定的脚本。
If myshell.sh
is executed like:
如果myshell.sh
执行如下:
myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3
myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3
All of the above should be able to call as
以上所有应该都可以调用为
test.sh param1 param2 param3
Is it possible to utilize the options parameters in themyshell.sh
and post remaining parameters to underlying script?
是否可以利用中的选项参数myshell.sh
并将剩余参数发布到底层脚本?
回答by DRendar
Sorry for commenting on an old thread, but thought I'd post for those, like me who were searching on how to do this...
很抱歉对旧线程发表评论,但我想我会为那些像我这样正在寻找如何做到这一点的人发帖...
I wanted to do something similar to the OP, and I found the relevant information I required hereand here
我想做一些类似于OP的事情,我在这里和这里找到了我需要的相关信息
Essentially if you want to do something like:
基本上,如果你想做这样的事情:
script.sh [options] ARG1 ARG2
Then get your options like this:
然后让你的选择是这样的:
while getopts "h:u:p:d:" flag; do
case "$flag" in
h) HOSTNAME=$OPTARG;;
u) USERNAME=$OPTARG;;
p) PASSWORD=$OPTARG;;
d) DATABASE=$OPTARG;;
esac
done
And then you can get your positional arguments like this:
然后你可以得到这样的位置参数:
ARG1=${@:$OPTIND:1}
ARG2=${@:$OPTIND+1:1}
More information and details are available through the link above.
更多信息和详细信息可通过上面的链接获得。
Hope that helps!!
希望有帮助!!
回答by chichi
Mix opts and args :
混合 opts 和 args :
ARGS=""
echo "options :"
while [ $# -gt 0 ]
do
unset OPTIND
unset OPTARG
while getopts as:c: options
do
case $options in
a) echo "option a no optarg"
;;
s) serveur="$OPTARG"
echo "option s = $serveur"
;;
c) cible="$OPTARG"
echo "option c = $cible"
;;
esac
done
shift $((OPTIND-1))
ARGS="${ARGS} "
shift
done
echo "ARGS : $ARGS"
exit 1
Result:
结果:
bash test.sh -a arg1 arg2 -s serveur -c cible arg3
options :
option a no optarg
option s = serveur
option c = cible
ARGS : arg1 arg2 arg3
回答by rush
getopts
won't parse the mix of param1
and -n
options.
getopts
不会解析param1
和-n
选项的组合。
It is much better to put param1-3 into options like others.
像其他人一样将 param1-3 放入选项中要好得多。
Furthermore you can use already existing libraries such as shflags. It is pretty smart and it is easy to use.
此外,您可以使用现有的库,例如shflags。它非常智能且易于使用。
And the last way is to write your own function to parse params without getopts, just iterating all params through case
construction. It is the hardest way but it is the only way to match your expectations exactly.
最后一种方法是编写自己的函数来解析没有 getopts 的参数,只需通过case
构造迭代所有参数。这是最困难的方法,但它是完全符合您的期望的唯一方法。
回答by Michael Kropat
I thought up one way that getopts
can be extended to truly mix options and positional parameters. The idea is to alternate between calling getopts
and assigning any positional parameters found to n1
, n2
, n3
, etc.:
我想出了一种getopts
可以扩展到真正混合选项和位置参数的方法。这样做是为了交替调用之间getopts
和分配任何位置参数发现n1
,n2
,n3
等:
parse_args() {
_parse_args 1 "$@"
}
_parse_args() {
local n=""
shift
local options_func=""
shift
local OPTIND
"$options_func" "$@"
shift $(( OPTIND - 1 ))
if [ $# -gt 0 ]; then
eval test -n ${n$n+x}
if [ $? -eq 0 ]; then
eval n$n="$1"
fi
shift
_parse_args $(( n + 1 )) "$options_func" "$@"
fi
}
Then in the OP's case, you could use it like:
然后在 OP 的情况下,您可以像这样使用它:
main() {
local n1='' n2='' n3=''
local duration hostname script
parse_args parse_main_options "$@"
echo "n1 = $n1"
echo "n2 = $n2"
echo "n3 = $n3"
echo "duration = $duration"
echo "hostname = $hostname"
echo "script = $script"
}
parse_main_options() {
while getopts d:h:s: opt; do
case "$opt" in
d) duration="$OPTARG" ;;
h) hostname="$OPTARG" ;;
s) script="$OPTARG" ;;
esac
done
}
main "$@"
Running it shows the output:
运行它显示输出:
$ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
n1 = param1
n2 = param2
n3 = param3
duration = waittime
hostname = hostname
script = test.sh
Just a proof of concept, but maybe it's useful to someone.
只是一个概念证明,但也许对某人有用。
Note:there's a gotcha if one function that uses parse_args
calls another function that uses parse_args
andthe outer function declares e.g. local n4=''
, but the inner one doesn't and4 or more positional parameters are passed to the inner function
注意:如果一个使用的函数parse_args
调用另一个使用的函数parse_args
并且外部函数声明了 eg local n4=''
,则有一个问题,但内部函数没有,并且将4 个或更多位置参数传递给内部函数
回答by Vlad
Just mashed up a quickie, which easily handles a mixture of options and positional-parameters (leaving only positional-params in $@):
刚刚混搭了一个 quickie,它可以轻松处理选项和位置参数的混合(仅在 $@ 中留下位置参数):
#!/bin/bash
while [ ${#} -gt 0 ];do OPTERR=0;OPTIND=1;getopts "p:o:hvu" arg;case "$arg" in
p) echo "Path: [$OPTARG]" ;;
o) echo "Output: [$OPTARG]" ;;
h) echo "Help" ;;
v) echo "Version" ;;
\?) SET+=("") ;;
*) echo "Coding error: '-$arg' is not handled by case">&2 ;;
esac;shift;[ "" != "$OPTARG" ] && shift;done
[ ${#SET[@]} -gt 0 ] && set "" "${SET[@]}" && shift
echo -e "=========\nLeftover (positional) parameters (count=$#) are:"
for i in `seq $#`;do echo -e "\t$i> [${!i}]";done
Sample output:
示例输出:
[root@hots:~]$ ./test.sh 'aa bb' -h -v -u -q 'cc dd' -p 'ee ff' 'gg hh' -o ooo
Help
Version
Coding error: '-u' is not handled by case
Path: [ee ff]
Output: [ooo]
=========
Leftover (positional) parameters (count=4) are:
1> [aa bb]
2> [-q]
3> [cc dd]
4> [gg hh]
[root@hots:~]$
回答by user
Instead of using getopts
, you can directly implement your own bash argument parser. Take this as a working example. It can handle simultaneously name and position arguments.
getopts
您可以直接实现自己的 bash 参数解析器,而不是使用。以此为工作示例。它可以同时处理名称和位置参数。
#!/bin/bash
function parse_command_line() {
local named_options;
local parsed_positional_arguments;
yes_to_all_questions="";
parsed_positional_arguments=0;
named_options=(
"-y" "--yes"
"-n" "--no"
"-h" "--help"
"-s" "--skip"
"-v" "--version"
);
function validateduplicateoptions() {
local item;
local variabletoset;
local namedargument;
local argumentvalue;
variabletoset="";
namedargument="";
argumentvalue="";
if [[ -z "${namedargument}" ]]; then
printf "Error: Missing command line option for named argument '%s', got '%s'...\n" "${variabletoset}" "${argumentvalue}";
exit 1;
fi;
for item in "${named_options[@]}";
do
if [[ "${item}" == "${argumentvalue}" ]]; then
printf "Warning: Named argument '%s' got possible invalid option '%s'...\n" "${namedargument}" "${argumentvalue}";
exit 1;
fi;
done;
if [[ -n "${!variabletoset}" ]]; then
printf "Warning: Overriding the named argument '%s=%s' with '%s'...\n" "${namedargument}" "${!variabletoset}" "${argumentvalue}";
else
printf "Setting '%s' named argument '%s=%s'...\n" "${thing_name}" "${namedargument}" "${argumentvalue}";
fi;
eval "${variabletoset}='${argumentvalue}'";
}
# https://stackoverflow.com/questions/2210349/test-whether-string-is-a-valid-integer
function validateintegeroption() {
local namedargument;
local argumentvalue;
namedargument="";
argumentvalue="";
if [[ -z "" ]];
then
argumentvalue="";
fi;
if [[ -n "$(printf "%s" "${argumentvalue}" | sed s/[0-9]//g)" ]];
then
if [[ -z "" ]];
then
printf "Error: The %s positional argument requires a integer, but it got '%s'...\n" "${parsed_positional_arguments}" "${argumentvalue}";
else
printf "Error: The named argument '%s' requires a integer, but it got '%s'...\n" "${namedargument}" "${argumentvalue}";
fi;
exit 1;
fi;
}
function validateposisionaloption() {
local variabletoset;
local argumentvalue;
variabletoset="";
argumentvalue="";
if [[ -n "${!variabletoset}" ]]; then
printf "Warning: Overriding the %s positional argument '%s=%s' with '%s'...\n" "${parsed_positional_arguments}" "${variabletoset}" "${!variabletoset}" "${argumentvalue}";
else
printf "Setting the %s positional argument '%s=%s'...\n" "${parsed_positional_arguments}" "${variabletoset}" "${argumentvalue}";
fi;
eval "${variabletoset}='${argumentvalue}'";
}
while [[ "${#}" -gt 0 ]];
do
case in
-y|--yes)
yes_to_all_questions="";
printf "Named argument '%s' for yes to all questions was triggered.\n" "";
;;
-n|--no)
yes_to_all_questions="";
printf "Named argument '%s' for no to all questions was triggered.\n" "";
;;
-h|--help)
printf "Print help here\n";
exit 0;
;;
-s|--skip)
validateintegeroption "" "";
validateduplicateoptions g_installation_model_skip_commands "" "";
shift;
;;
-v|--version)
validateduplicateoptions branch_or_tag "" "";
shift;
;;
*)
parsed_positional_arguments=$((parsed_positional_arguments+1));
case ${parsed_positional_arguments} in
1)
validateposisionaloption branch_or_tag "";
;;
2)
validateintegeroption "";
validateposisionaloption g_installation_model_skip_commands "";
;;
*)
printf "ERROR: Extra positional command line argument '%s' found.\n" "";
exit 1;
;;
esac;
;;
esac;
shift;
done;
if [[ -z "${g_installation_model_skip_commands}" ]];
then
g_installation_model_skip_commands="0";
fi;
}
You would call this function as:
您可以将这个函数称为:
#!/bin/bash
source ./function_file.sh;
parse_command_line "${@}";
Usage example:
用法示例:
./test.sh as 22 -s 3
Setting the 1 positional argument 'branch_or_tag=as'...
Setting the 2 positional argument 'skip_commands=22'...
Warning: Overriding the named argument '-s=22' with '3'...
References:
参考:
回答by Yuriy Pozniak
You can try this trick: after while loop with optargs, just use this snippet
你可以试试这个技巧:在带有 optargs 的 while 循环之后,只需使用这个片段
#shift away all the options so that only positional agruments
#remain in $@
for (( i=0; i<OPTIND-1; i++)); do
shift
done
POSITIONAL="$@"
However, this approach has a bug:
但是,这种方法有一个错误:
- all the options after the first positional argument are ingored by getopts and are considered as positional arguments - event those that are correct (see sample output: -m and -c are among positional arguments)
- 第一个位置参数之后的所有选项都由 getopts 分析并被视为位置参数 - 事件那些是正确的(参见示例输出:-m 和 -c 在位置参数中)
Maybe it has even more bugs...
也许它有更多的错误......
Look at the whole example:
看整个例子:
while getopts :abc opt; do
case $opt in
a)
echo found: -a
;;
b)
echo found: -b
;;
c)
echo found: -c
;;
\?) echo found bad option: -$OPTARG
;;
esac
done
#OPTIND-1 now points to the first arguments not beginning with -
#shift away all the options so that only positional agruments
#remain in $@
for (( i=0; i<OPTIND-1; i++)); do
shift
done
POSITIONAL="$@"
echo "positional: $POSITIONAL"
Output:
输出:
[root@host ~]# ./abc.sh -abc -de -fgh -bca haha blabla -m -c
found: -a
found: -b
found: -c
found bad option: -d
found bad option: -e
found bad option: -f
found bad option: -g
found bad option: -h
found: -b
found: -c
found: -a
positional: haha blabla -m -c
回答by Henk Langeveld
There are some standards for unix option processing, and in shell programming, getopts
is the best way of enforcing them. Almost any modern language (perl, python) has a variant on getopts
.
unix 选项处理有一些标准,在 shell 编程中,这getopts
是强制执行它们的最佳方式。几乎所有现代语言(perl、python)在getopts
.
This is just a quick example:
这只是一个简单的例子:
command [ options ] [--] [ words ]
Each option must start with a dash,
-
, and must consist of a single character.The GNU project introduced Long Options, starting with two dashes
--
, followed by a whole word,--long_option
. The AST KSH project has a getopts that also supports long options, andlong options starting with a single dash,-
, as infind(1)
.Options may or may not expect arguments.
Any word not starting with a dash,
-
, will end option processing.The string
--
must be skipped and will end option processing.Any remaining arguments are left as positional parameters.
每个选项必须以破折号、 开头
-
,并且必须由单个字符组成。GNU 项目引入了长选项,以两个破折号开头
--
,后跟一个完整的单词--long_option
. AST KSH 项目有一个 getopts,它也支持长选项,以及以单个破折号开头的长选项-
,如find(1)
.选项可能需要也可能不需要参数。
任何不以破折号 , 开头的单词
-
都将结束选项处理。--
必须跳过该字符串并将结束选项处理。任何剩余的参数都保留为位置参数。
The Open Group has a section on Utility Argument Syntax
开放组有一个部分关于 Utility Argument Syntax
Eric Raymond's The Art of Unix Programming has a chapteron traditional unix choices for option letters and their meaning.
Eric Raymond 的 The Art of Unix Programming有一章关于选项字母的传统 unix 选择及其含义。