我正在准备一个脚本getopt
。我想添加帮助部分。因此,如果他们使用--help
或-h
应该执行该函数(只需打印说明)并返回它。
示例代码:
log_type='unset'
state='unset'
date='unset'
help='unset'
mode="$1"
usage()
{
echo "Usage: LogRotator [ -t | --log_type - Allowed values: [access error] ]
[ -s | --state - Allowed values: [archive or backup] ]
[ -d | --date 1-10-2020]
[ -h | --help]
Modes:
1) list - list the files
2) restore - restore the files"
exit 2
}
ARGUMENT_LIST=(
"log-type"
"state"
"date"
)
# read arguments
opts=$(getopt \
--longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")",help \
--name "$(basename "$0")" \
--options "" \
-- "$@"
)
VALID_ARGUMENTS=$?
if [ "$VALID_ARGUMENTS" != "0" ]; then
usage
fi
eval set --$opts
while [[ $# -gt 0 ]]; do
case "$1" in
-t | --log-type)
log_type=$2
shift 2
;;
-s | --state)
state=$2
shift 2
;;
-d | --date)
date=$2
shift 2
;;
-h | --help)
help=1
shift
;;
*)
break
;;
esac
done
if [[ "$help" == 1 ]]
then
usage
fi
上面的脚本工作正常,但我添加了一个单独的部分来检查和调用使用函数,我不确定这是否是一个好的做法。
另外,另一个问题是,它不接受单个-
标志(如 -h -t)
我尝试添加-o tsdh
但没有成功。
预期输出:
./script -h
Usage: LogRotator [ -t | --log_type - Allowed values: [access error] ]
[ -s | --state - Allowed values: [archive or backup] ]
[ -d | --date 1-10-2020]
Modes:
1) list - list the files
2) restore - restore the files
答案1
我将其写为代码审查:
首先,您没有指定 shebang。如果没有 shebang,脚本将由sh
.您的代码不符合sh
语法。语法最相似的 shell 是zsh
.不过,如果不是未加引号的$opts
,它也可以在 ksh93 和 bash 中工作。我想它是为 bash 设计的,因为zsh
会有更好的方法来编写它。
所以在这里:
#! /bin/bash -
或者:
#! /usr/bin/env bash
支持bash
不在/bin
.
log_type='unset' state='unset' date='unset' help='unset'
在这里,如果您使用特定字符串作为默认值,则将无法消除用户未指定选项但值为 的情况unset
。
在这里,我只想做
unset -v log_type state date help
虽然对于布尔值,我更喜欢help=false
.
mode="$1"
在这里,您将第一个参数存储在 中$mode
,但不检查它是否存在,也不将其从参数列表中删除,这意味着getopt
也会收到它!
或许:
case $1 in
(list | restore) mode=$1; shift;;
(*) usage;;
esac
(在函数声明之后usage
)。
usage() { echo "Usage: LogRotator [ -t | --log_type - Allowed values: [access error] ] [ -s | --state - Allowed values: [archive or backup] ]
请注意,虽然这些[ -t
和[ -s
在脚本的源代码中对齐,但由于缩进,它们不会出现在输出中。另外,您将像此处那样对脚本的名称进行硬编码LogRotator
,但稍后使用basename
of 。$0
另外,该使用消息应该发送到 stderr,因此:
PROGNAME=${0##*/}
usage() {
cat << EOF >&2
Usage: $PROGNAME ...
EOF
[ -d | --date 1-10-2020]
这1-10-2020
可能是最糟糕的日期格式。首先它是模棱两可的。大多数人会将其理解为 10 月 1 日,但在北美部分地区,则会将其理解为 1 月 10 日。此外,这些字符串按时间顺序排序与按词法排序不同(即使在您对字段重新排序后,因为这些部分不是用零填充的)。
日期有一种国际格式。 2020-10-01 将成为标准,被大多数人和公用事业公司认可,并且按词汇顺序排序与按时间顺序排序相同。
[...]
ARGUMENT_LIST=( "log-type" "state" "date" )
最好保留所有大写变量环境变量。
# read arguments opts=$(getopt \ --longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")",help \
在这里,您将附加:,
到 的所有元素$ARGUMENT_LIST
,因此将得到log-type:,state:,date:,,help
。
--name "$(basename "$0")" \
应该是"$(basename -- "$0"}"
或者${0##*/}
这里。这"$PROGNAME"
是我们之前定义的。
--options "" \
这是针对单字符选项的,因此您需要在此处指定它们:
--options hs:d:t:
-- "$@" ) VALID_ARGUMENTS=$? if [ "$VALID_ARGUMENTS" != "0" ]; then
if
检查命令是否成功。这就是它的作用。
所以就:
if
! opts=$(
...
)
then
usage
fi
要不就:
opts=$(...) || usage
这里。
usage fi eval set --$opts
不加引号的参数扩展会调用 split+glob ,这在这里没有意义。另外,您需要将其--
与此处的其余部分分开。就是set -- <contents-of-$opts>
你想被评估为 shell 代码,所以:
eval "set -- $opts"
while [[ $# -gt 0 ]]; do case "$1" in -t | --log-type) log_type=$2
您需要根据此处允许的设置检查提供的值,以警告用户是否使用-t blah -t error
.您还可以警告用户仅考虑最后指定的类型。
shift 2 ;; -s | --state) state=$2 shift 2 ;; -d | --date) date=$2
注意缩进。一致的缩进使代码更易于阅读并有助于避免一些错误。
shift 2 ;; -h | --help) help=1 shift ;; *) break
应该shift; break
在这里,因为getopt
会添加一个--
来告诉您选项结束的位置。
;; esac done
此时,您可能需要检查是否有更多参数可用,如果不符合预期则报告错误:
[ "$#" -eq 0 ] || usage
if [[ "$help" == 1 ]] then usage fi
如果您使用了help=false
/ help=true
,那么那就是:
if "$help"; then
usage
fi
答案2
我通过在 中添加简短选项来更正您的脚本getopt
,请注意,:
在每个之后opt
意味着该选项需要一个参数:
#!/bin/bash
log_type='unset'
state='unset'
date='unset'
help='unset'
mode="$1"
usage()
{
echo "Usage: LogRotator [ -t | --log_type - Allowed values: [access error] ]
[ -s | --state - Allowed values: [archive or backup] ]
[ -d | --date 1-10-2020]
[ -h | --help]
Modes:
1) list - list the files
2) restore - restore the files"
exit 2
}
ARGUMENT_LIST=(
"log-type"
"state"
"date"
)
# read arguments
opts=$(getopt \
-o t:s:d:h \
--longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")",help \
--name "$(basename "$0")" \
-- "$@"
)
VALID_ARGUMENTS=$?
if [ "$VALID_ARGUMENTS" != "0" ]; then
usage
fi
eval set -- $opts
while [[ $# -gt 0 ]]; do
case "$1" in
-t | --log-type)
log_type=$2
shift 2
;;
-s | --state)
state=$2
shift 2
;;
-d | --date)
date=$2
shift 2
;;
-h | --help)
help=1
shift
;;
*)
break
;;
esac
done
if [[ "$help" == 1 ]]
then
usage
fi