我希望能够在我的脚本中接受强制和可选标志。这是我到目前为止所拥有的。
#!bin/bash
while getopts ":a:b:cdef" opt; do
case $opt in
a ) APPLE="$OPTARG";;
b ) BANANA="$OPTARG";;
c ) CHERRY="$OPTARG";;
d ) DFRUIT="$OPTARG";;
e ) EGGPLANT="$OPTARG";;
f ) FIG="$OPTARG";;
\?) echo "Invalid option: -"$OPTARG"" >&2
exit 1;;
: ) echo "Option -"$OPTARG" requires an argument." >&2
exit 1;;
esac
done
echo "Apple is "$APPLE""
echo "Banana is "$BANANA""
echo "Cherry is "$CHERRY""
echo "Dfruit is "$DFRUIT""
echo "Eggplant is "$EGGPLANT""
echo "Fig is "$FIG""
但是,输出如下:
bash script.sh -a apple -b banana -c cherry -d dfruit -e eggplant -f fig
...输出:
Apple is apple
Banana is banana
Cherry is
Dfruit is
Eggplant is
Fig is
正如您所看到的,可选标志不会像使用$ OPTARG那样使用所需的标志来提取参数。有没有办法在可选标志上读取$ OPTARG而不摆脱整齐的“:)”错误处理?
编辑:我按照下面的吉尔伯特的建议结束了。这是我做的:
#!/bin/bash
if [[ "$1" =~ ^((-{1,2})([Hh]$|[Hh][Ee][Ll][Pp])|)$ ]]; then
print_usage; exit 1
else
while [[ $# -gt 0 ]]; do
opt="$1"
shift;
current_arg="$1"
if [[ "$current_arg" =~ ^-{1,2}.* ]]; then
echo "WARNING: You may have left an argument blank. Double check your command."
fi
case "$opt" in
"-a"|"--apple" ) APPLE="$1"; shift;;
"-b"|"--banana" ) BANANA="$1"; shift;;
"-c"|"--cherry" ) CHERRY="$1"; shift;;
"-d"|"--dfruit" ) DFRUIT="$1"; shift;;
"-e"|"--eggplant" ) EGGPLANT="$1"; shift;;
"-f"|"--fig" ) FIG="$1"; shift;;
* ) echo "ERROR: Invalid option: \""$opt"\"" >&2
exit 1;;
esac
done
fi
if [[ "$APPLE" == "" || "$BANANA" == "" ]]; then
echo "ERROR: Options -a and -b require arguments." >&2
exit 1
fi
非常感谢大家。这到目前为止完美无缺。
答案 0 :(得分:37)
:
表示“接受论证”,而非“强制性论证”。也就是说,:
后面跟着的选项字符表示标志式选项(无参数),而后跟:
的选项字符表示带参数的选项。
因此,你可能想要
getopts "a:b:c:d:e:f:" opt
如果你想要“强制性”选项(有点矛盾),你可以在解析参数后检查你的强制选项值是否都已设置。
答案 1 :(得分:8)
这并不容易......任何"可选"在getopts
知道的情况下,实际上必须要求选项参数。当然,可选参数必须是脚本的相同参数的一部分,而不是它所使用的选项。否则,带有可选参数的选项-f
和带有必需参数的选项-a
可能会混淆:
# Is -a an option or an argument?
./script.sh -f -a foo
# -a is definitely an argument
./script.sh -f-a foo
执行此操作的唯一方法是测试选项及其参数是否与脚本位于同一参数中。如果是这样,OPTARG就是该选项的参数。否则,OPTIND必须减1。当然,该选项现在需要有一个参数,这意味着当一个选项缺少一个参数时会找到一个字符。只需使用另一个case
来确定是否需要任何选项:
while getopts ":a:b:c:d:e:f:" opt; do
case $opt in
a) APPLE="$OPTARG";;
b) BANANA="$OPTARG";;
c|d|e|f)
if test "$OPTARG" = "$(eval echo '$'$((OPTIND - 1)))"; then
OPTIND=$((OPTIND - 1))
else
case $opt in
c) CHERRY="$OPTARG";;
d) DFRUIT="$OPTARG";;
...
esac
fi ;;
\?) ... ;;
:)
case "$OPTARG" in
c|d|e|f) ;; # Ignore missing arguments
*) echo "option requires an argument -- $OPTARG" >&2 ;;
esac ;;
esac
done
到目前为止,这对我有用。
答案 2 :(得分:4)
大多数shell getopts很长一段时间都让我烦恼,包括缺乏对可选参数的支持。
但如果您愿意使用“--posix”样式参数,请访问bash argument case for args in $@
答案 3 :(得分:4)
bash
的{{1}} getopts
的{{1}}手册页(引用版本4.1手册)说:
shell脚本使用
bash
getopts
getopts optstring name
来解析位置参数。 optstring 包含 要识别的选项字符;如果一个字符后跟一个 冒号,期望选项有一个参数,应该分开 从它的白色空间。冒号(':')和问号('?')可能不是 用作选项字符。每次调用它时,[args]
都会放置下一个 shell变量 name 中的选项,初始化 name (如果它不存在),以及 要处理到变量getopts
的下一个参数的索引。getopts
每次调用shell或shell脚本时,都会初始化为1。当一个 选项需要参数,OPTIND
将该参数放入变量中OPTIND
。 shell不会自动重置getopts
;它必须是手动的 如果a,在同一个shell调用中多次调用OPTARG
之间重置 将使用新的参数集。遇到选项结束时,
OPTIND
退出并返回值 大于零。getopts
设置为第一个非选项参数的索引, 并且 name 设置为'?'。
getopts
通常会解析位置参数,但是如果有更多的参数 在 args 中给出,OPTIND
会解析这些。
getopts
可以通过两种方式报告错误。如果 optstring 的第一个字符是a 冒号,使用静默错误报告。在正常操作诊断消息中 在遇到无效选项或缺少选项参数时打印。 如果变量getopts
设置为0,则不会显示任何错误消息,即使是 optstring 的第一个字符不是冒号。如果看到无效选项,
getopts
将'?'放入名称,如果不是沉默, 打印错误消息并取消设置OPTERR
。如果getopts
是静默的,则选项 找到的字符放在OPTARG
中,不会打印诊断消息。 如果找不到必需的参数,并且getopts
不是静音,则为问号 ('?')放在名称中,未设置OPTARG
,并打印诊断消息。如果getopts
是静默的,然后冒号(':')放在 name 中,OPTARG
设置为 找到了选项字符。
请注意:
选项字符串中的前导冒号将getopts
置于静默模式;它不会生成任何错误消息。
该描述未提及有关可选选项参数的任何内容。
我假设你的功能类似于:
OPTARG
其中标志getopts
(script -ffilename
script -f
)可选地接受参数。 f
的{{1}}命令不支持此功能。 POSIX函数-f
几乎不支持该表示法。实际上,只有命令行上的最后一个选项才能在POSIX下有一个可选参数。
部分参考Using getopts
in bash
shell script to get long and short command-line options。
GNU getopt
(单数!)程序是一个复杂的beastie,支持long和short选项,并支持长选项的可选参数(并使用GNU getopt(3)
。跟踪它的来源是有趣的;链接在die.net的页面上是错的;你会在ftp://ftp.kernel.org/pub/linux/utils/util-linux下的子目录中找到它(没有bash
)。我没有在http://www.gnu.org/找到一个位置或包含它的http://www.fsf.org/。
答案 4 :(得分:4)
对于bash,这是我最喜欢的解析/支持cli args的方法。我使用了getopts,它太令人沮丧,因为它不支持长选项。我确实喜欢它的工作原理 - 特别是对于内置功能。</ p>
usage()
{
echo "usage: $0 -OPT1 <opt1_arg> -OPT2"
}
while [ "`echo $1 | cut -c1`" = "-" ]
do
case "$1" in
-OPT1)
OPT1_ARGV=$2
OPT1_BOOL=1
shift 2
;;
-OPT2)
OPT2_BOOL=1
shift 1
;;
*)
usage
exit 1
;;
esac
done
简短,简单。工程师最好的朋友!
我认为这可以修改为支持“ - ”选项......
干杯=)
答案 5 :(得分:-1)
#!/bin/bash
while getopts ":a:b:c:d:e:f:" opt; do
case $opt in
a ) APPLE="$OPTARG";;
b ) BANANA="$OPTARG";;
c ) CHERRY="$OPTARG";;
d ) DFRUIT="$OPTARG";;
e ) EGGPLANT="$OPTARG";;
f ) FIG="$OPTARG";;
\?) echo "Invalid option: -"$OPTARG"" >&2
exit 1;;
: ) echo "Option -"$OPTARG" requires an argument." >&2
exit 1;;
esac
done
echo "Apple is "$APPLE""
echo "Banana is "$BANANA""
echo "Cherry is "$CHERRY""
echo "Dfruit is "$DFRUIT""
echo "Eggplant is "$EGGPLANT""
echo "Fig is "$FIG""