如何使用内置 Bash getopts 的长选项?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12022592/
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
How can I use long options with the Bash getopts builtin?
提问by Hemang
I am trying to parse a -temp
option with Bash getopts. I'm calling my script like this:
我正在尝试-temp
使用 Bash getopts解析一个选项。我这样调用我的脚本:
./myscript -temp /foo/bar/someFile
Here is the code I'm using to parse the options.
这是我用来解析选项的代码。
while getopts "temp:shots:o:" option; do
case $option in
temp) TMPDIR="$OPTARG" ;;
shots) NUMSHOTS="$OPTARG" ;;
o) OUTFILE="$OPTARG" ;;
*) usage ;;
esac
done
shift $(($OPTIND - 1))
[ $# -lt 1 ] && usage
回答by mcoolive
As other people explained, getopts doesn't parse long options. You can use getopt, but it's not portable (and it is broken on some platform...)
正如其他人解释的那样, getopts 不解析长选项。您可以使用 getopt,但它不可移植(并且在某些平台上已损坏...)
As a workaround, you can implement a shell loop. Here an example that transforms long options to short ones before using the standard getopts command (it's simpler in my opinion):
作为一种解决方法,您可以实现一个 shell 循环。这是一个在使用标准 getopts 命令之前将长选项转换为短选项的示例(我认为它更简单):
# Transform long options to short ones
for arg in "$@"; do
shift
case "$arg" in
"--help") set -- "$@" "-h" ;;
"--rest") set -- "$@" "-r" ;;
"--ws") set -- "$@" "-w" ;;
*) set -- "$@" "$arg"
esac
done
# Default behavior
rest=false; ws=false
# Parse short options
OPTIND=1
while getopts "hrw" opt
do
case "$opt" in
"h") print_usage; exit 0 ;;
"r") rest=true ;;
"w") ws=true ;;
"?") print_usage >&2; exit 1 ;;
esac
done
shift $(expr $OPTIND - 1) # remove options from positional parameters
回答by geirha
getopts
can only parse short options.
getopts
只能解析短选项。
Most systems also have an external getopt
command, but getopt is not standard, and is generally broken by design as it can't handle all arguments safely (arguments with whitespace and empty arguments), only GNU getopt can handle them safely, but only if you use it in a GNU-specific way.
大多数系统也有一个外部getopt
命令,但 getopt 不是标准的,并且通常被设计破坏,因为它不能安全地处理所有参数(带有空格和空参数的参数),只有 GNU getopt 可以安全地处理它们,但前提是你以特定于 GNU 的方式使用它。
The easier choice is to use neither, just iterate the script's arguments with a while-loop and do the parsing yourself.
更简单的选择是两者都不使用,只需使用 while 循环迭代脚本的参数并自己进行解析。
See http://mywiki.wooledge.org/BashFAQ/035for an example.
回答by Stephane Rouberol
getopts
is used by shell procedures to parse positional parameters of 1 character only
(no GNU-style long options (--myoption) or XF86-style long options (-myoption))
getopts
shell 程序使用它来解析只有 1 个字符的位置参数(没有 GNU 风格的长选项 (--myoption) 或 XF86 风格的长选项 (-myoption))
回答by Ashish Shetkar
the simplest way to achieve this is with the help of getoptand --longoptions
最简单的方法是借助getopt和--longoptions
try this , hope this is useful
试试这个,希望这是有用的
# Read command line options
ARGUMENT_LIST=(
"input1"
"input2"
"input3"
)
# read arguments
opts=$(getopt \
--longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")" \
--name "$(basename "myscript.sh --input1 "ABC" --input2 "PQR" --input2 "XYZ"
")" \
--options "" \
-- "$@"
)
echo $opts
eval set --$opts
while true; do
case "" in
--input1)
shift
empId=
;;
--input2)
shift
fromDate=
;;
--input3)
shift
toDate=
;;
--)
shift
break
;;
esac
shift
done
and this is how - you call the shell script
这就是如何 - 你调用 shell 脚本
usage() { echo usage: error from ; exit -1; }
OPTIND=1
PREVOPTIND=$OPTIND
while getopts "t:s:o:" option; do
GAP=$((OPTIND-(PREVOPTIND+1)))
case $option in
t) case "${OPTARG}" in
emp) # i.e. -temp
((GAP)) && usage "-${option} and ${OPTARG}"
TMPDIR="$OPTARG"
;;
*)
true
;;
esac
;;
s) case "${OPTARG}" in
hots) # i.e. -shots
((GAP)) && usage
NUMSHOTS="$OPTARG"
;;
*) usage "-${option} and ${OPTARG}" ;;
esac
;;
o) OUTFILE="$OPTARG" ;;
*) usage "-${option} and ${OPTARG}" ;;
esac
PREVOPTIND=$OPTIND
done
shift $(($OPTIND - 1))
回答by user2023370
Although this question was posted over 2 years ago, I found myself needing support for XFree86-style long options too; and I also wanted to take what I could from getopts. Consider the GCC switch -rdynamic
. I mark r
as the flag letter, and expect dynamic
within $OPTARG
...but, I want to reject -r dynamic
, while accepting other options following r
.
尽管这个问题是在 2 年前发布的,但我发现自己也需要支持 XFree86 风格的长选项;我也想从 getopts 中获取我能做的。考虑 GCC 开关-rdynamic
。我标记r
为标志字母,并期望dynamic
在$OPTARG
...但是,我想拒绝-r dynamic
,同时接受以下其他选项r
。
The idea I've put below builds on the observation that $OPTIND
will be one larger than otherwise if space (a gap) follows the flag. So, I define a bash variable to hold the previous value of $OPTIND
, called $PREVOPTIND
, and update it at the end of the while
loop. If $OPTIND
is 1
greater than $PREVOPTIND
, we have no gap (i.e. -rdynamic
); and $GAP
is set to false
. If instead $OPTIND
is 2
greater than $PREVOPTIND
, we dohave a gap (e.g. -r dynamic
), and $GAP
is set to true
.
我在下面提出的想法建立在观察的基础上,$OPTIND
如果空间(间隙)跟随标志,则该观察将比其他情况大。因此,我定义了一个 bash 变量来保存 的先前值$OPTIND
,称为$PREVOPTIND
,并在while
循环结束时更新它。如果$OPTIND
是1
大于$PREVOPTIND
,我们没有任何间隙(即-rdynamic
); 并$GAP
设置为false
。相反,如果$OPTIND
是2
大于$PREVOPTIND
,我们做的有一定的差距(例如-r dynamic
),并$GAP
设置为true
。
# convert long word options to short word for ease of use and portability
for argu in "$@"; do
shift
#echo "curr arg = "
case "$argu" in
"-start"|"--start")
# param=param because no arg is required
set -- "$@" "-s"
;;
"-pb"|"--pb"|"-personalbrokers"|"--personalbrokers")
# pb +arg required
set -- "$@" "-p "; #echo "arg=$argu"
;;
"-stop"|"--stop")
# param=param because no arg is required
set -- "$@" "-S"
;;
# the catch all option here removes all - symbols from an
# argument. if an option is attempted to be passed that is
# invalid, getopts knows what to do...
*) [[ $(echo $argu | grep -E "^-") ]] && set -- "$@" "${argu//-/}" || echo "no - symbol. not touching $argu" &>/dev/null
;;
esac
done
#echo -e "\n final option conversions = $@\n"
# remove options from positional parameters for getopts parsing
shift $(expr $OPTIND - 1)
declare -i runscript=0
# only p requires an argument hence the p:
while getopts "sSp:" param; do
[[ "$param" == "p" ]] && [[ $(echo $OPTARG | grep -E "^-") ]] && funcUsage "order"
#echo $param
#echo "OPTIND=$OPTIND"
case $param in
s)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it's arg $OPTARG"
arg0=start
;;
p)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it's arg $OPTARG"
[[ "$OPTARG" == "all" ]] && echo -e "argument \"$OPTARG\" accepted. continuing." && (( runscript += 1 )) || usage="open"
[[ $( echo $pbString | grep -w "$OPTARG" ) ]] && echo -e "pb $OPTARG was validated. continuing.\n" && (( runscript += 1 )) || usage="personal"
[[ "$runscript" -lt "1" ]] && funcUsage "$usage" "$OPTARG"
arg0=start
;;
S)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it's arg $OPTARG"
arg0=stop
;;
*)
getoptsRan=1
funcUsage
echo -e "Invalid argument\n"
;;
esac
done
funcBuildExcludes "$@"
shift $((OPTIND-1))
回答by Benjamin West
thanks to @mcoolive .
感谢@mcoolive。
I was able to use your $@ idea to convert whole word and long options to single letter options. Wanted to note to anyone using this idea that I also had to include shift $(expr $OPTIND - 1)
prior to running the arguments through the getopts loop.
我能够使用您的 $@ 想法将整个单词和长选项转换为单字母选项。想提醒任何使用这个想法的人,shift $(expr $OPTIND - 1)
在通过 getopts 循环运行参数之前,我也必须包含这个想法。
Totally different purpose but this is working well.
完全不同的目的,但这运作良好。
$ ./test-args.sh --a1 a1 --a2 "a 2" --a3 --a4= --a5=a5 --a6="a 6"
a1 = "a1"
a2 = "a 2"
a3 = "TRUE"
a4 = ""
a5 = "a5"
a6 = "a 6"
a7 = ""
回答by lmcarreiro
A simple DIY if you get trouble to use the built-in getopts
:
如果您在使用内置文件时遇到麻烦,请进行简单的 DIY getopts
:
Use:
用:
#!/bin/bash
function main() {
ARGS=`getArgs "$@"`
a1=`echo "$ARGS" | getNamedArg a1`
a2=`echo "$ARGS" | getNamedArg a2`
a3=`echo "$ARGS" | getNamedArg a3`
a4=`echo "$ARGS" | getNamedArg a4`
a5=`echo "$ARGS" | getNamedArg a5`
a6=`echo "$ARGS" | getNamedArg a6`
a7=`echo "$ARGS" | getNamedArg a7`
echo "a1 = \"$a1\""
echo "a2 = \"$a2\""
echo "a3 = \"$a3\""
echo "a4 = \"$a4\""
echo "a5 = \"$a5\""
echo "a6 = \"$a6\""
echo "a7 = \"$a7\""
exit 0
}
function getArgs() {
for arg in "$@"; do
echo "$arg"
done
}
function getNamedArg() {
ARG_NAME=
sed --regexp-extended --quiet --expression="
s/^--$ARG_NAME=(.*)$//p # Get arguments in format '--arg=value': [s]ubstitute '--arg=value' by 'value', and [p]rint
/^--$ARG_NAME$/ { # Get arguments in format '--arg value' ou '--arg'
n # - [n]ext, because in this format, if value exists, it will be the next argument
/^--/! p # - If next doesn't starts with '--', it is the value of the actual argument
/^--/ { # - If next do starts with '--', it is the next argument and the actual argument is a boolean one
# Then just repla[c]ed by TRUE
c TRUE
}
}
"
}
main "$@"
Script:
脚本:
#== set options ==#
SCRIPT_OPTS=':fbF:B:-:h'
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
[foo]=f
[bar]=b
[foobar]=F
[barfoo]=B
[help]=h
[man]=h
)
#== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
#== translate long options to short ==#
if [[ "x$OPTION" == "x-" ]]; then
LONG_OPTION=$OPTARG
LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2)
LONG_OPTIND=-1
[[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1)
[[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="$$LONG_OPTIND"
OPTION=${ARRAY_OPTS[$LONG_OPTION]}
[[ "x$OPTION" = "x" ]] && OPTION="?" OPTARG="-$LONG_OPTION"
if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then
OPTION=":" OPTARG="-$LONG_OPTION"
else
OPTARG="$LONG_OPTARG";
if [[ $LONG_OPTIND -ne -1 ]]; then
[[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
shift $OPTIND
OPTIND=1
fi
fi
fi
fi
#== options follow by another option instead of argument ==#
if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then
OPTARG="$OPTION" OPTION=":"
fi
#== manage options ==#
case "$OPTION" in
f ) foo=1 bar=0 ;;
b ) foo=0 bar=1 ;;
B ) barfoo=${OPTARG} ;;
F ) foobar=1 && foobar_name=${OPTARG} ;;
h ) usagefull && exit 0 ;;
: ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
esac
done
shift $((${OPTIND} - 1))
回答by Todd A. Jacobs
You can't use the getoptsBash builtin for long options--at least, not without having to build your own parsing functions. You should take a look at the /usr/bin/getoptbinary instead (provided on my system by the util-linuxpackage; your mileage may vary).
您不能将getoptsBash 内置用于长选项——至少,不必构建您自己的解析函数。您应该查看/usr/bin/getopt二进制文件(由util-linux包在我的系统上提供;您的里程可能会有所不同)。
See getopt(1) for specific invocation options.
有关特定调用选项,请参阅 getopt(1)。
回答by Michel VONGVILAY uxora.com
It's true that builtin bash getopts
only parse short options,
but you can still add few lines of scripting to make getopts handles long options.
确实,内置 bashgetopts
只解析短选项,但您仍然可以添加几行脚本来让 getopts 处理长选项。
Here is a part of code found in http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts
这是在http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts 中找到的部分代码
# Short options test
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2
# Long and short options test
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2
Here is a test:
这是一个测试:
##代码##Otherwise in recent Korn Shellksh93, getopts
can naturally parse long options and even display a man page alike. (see http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options)
否则在最近的 Korn Shellksh93 中,getopts
可以自然地解析长选项,甚至可以显示类似的手册页。(见http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options)
Michel VONGVILAY.
米歇尔·冯维莱。