为bash脚本提供了像命令一样接受标志的选项



我正在编写一个简单的bash脚本,我希望它能以任何顺序接受命令行中的参数。

我在网上浏览过,并在while循环中编写了一个带有case语句的简单函数。现在,"任意顺序"部分可以工作,但它只接受我设置的第一个参数。我肯定做错了什么,但脚本对我来说是全新的,我还没能弄清楚——非常感谢你的帮助。脚本的标志部分如下:

#Parameters - source,destination,credentials,bandwidth,timeout,port,help
flags () {
while test $# -gt 0; do
case "$1" in
-s|--source)
shift
if test $# -gt 0; then
export SOURCE=$1
else
echo "No source directory specified!"
exit 1
fi
;;
-d|--destination)
shift
if test $# -gt 0; then
export DESTINATION=$1
fi
;;
-c|--credentials)
shift
if test $# -gt 0; then
export CREDENTIALS=$1
fi
;;
-b|--bandwidth)
shift
if test $# -gt 0; then
export BANDWIDTH=$1
fi
;;
-t|--timeout)
shift
if test $# -gt 0; then
export TIMEOUT=$1
fi
;;
-p|--port)
shift
if test $# -gt 0; then
export PORT=$1
fi
;;
-h|--help)
shift
if test $# -gt 0; then
echo "Help goes here"
fi
;;
-l|--compression-level)
shift
if test $# -gt 0; then
export COMPRESS_LEVEL=$1
fi
;;
*)
break
;;
esac
done
}
flags "$@"
echo "source is $SOURCE, destination is $DESTINATION, credentials are $CREDENTIALS, bandwidth is $BANDWIDTH, timeout is $TIMEOUT, port is $PORT"

理想情况下,其中一些参数是强制性的,另一些是可选的,但这不是必须的。

我如何修复这个脚本以接受任何顺序的参数(理想情况下包括长形式和短形式(?

如注释中所述,在使用参数(例如凭据(后,需要进行另一次转换。对于不存在的参数,您应该在错误报告中保持一致。如果您得到-h--help,您应该简单地打印帮助并退出;你不应该测试更多的论点。如果有人请求帮助,你会给予帮助,不做任何其他事情。您还应该将错误回显为标准错误:echo "message" >&2。您的消息应该以脚本/程序名称为前缀:arg0=$(basename "$0" .sh)echo "$arg0: message" >&2等。

把这些变化放在一起,你可能会想出这样的脚本:

#!/bin/sh
arg0=$(basename "$0" .sh)
blnk=$(echo "$arg0" | sed 's/./ /g')
usage_info()
{
echo "Usage: $arg0 [{-s|--source} source] [{-d|--destination} destination] \"
echo "       $blnk [{-c|--credentials} credentials] [{-b|--bandwidth} bandwidth] \"
echo "       $blnk [{-t|--timeout} timeout] [{-p|--port} port] \"
echo "       $blnk [-h|--help] [{-l|--compression-level} level]"
}
usage()
{
exec 1>2   # Send standard output to standard error
usage_info
exit 1
}
error()
{
echo "$arg0: $*" >&2
exit 1
}
help()
{
usage_info
echo
echo "  {-s|--source} source            -- Set source directory (default: .)"
echo "  {-d|--destination} destination  -- Set destination"
echo "  {-c|--credentials} credentials  -- Set credentials"
echo "  {-b|--bandwidth} bandwidth      -- Set maximum bandwidth"
echo "  {-t|--timeout} timeout          -- Set timeout (default: 60s)"
echo "  {-p|--port} port                -- Set port number (default: 1234)"
echo "  {-l|--compression-level} level  -- Set compression level (default: 1)"
echo "  {-h|--help}                     -- Print this help message and exit"
#   echo "  {-V|--version}                  -- Print version information and exit"
exit 0
}
flags()
{
while test $# -gt 0
do
case "$1" in
(-s|--source)
shift
[ $# = 0 ] && error "No source directory specified"
export SOURCE="$1"
shift;;
(-d|--destination)
shift
[ $# = 0 ] && error "No destination specified"
export DESTINATION="$1"
shift;;
(-c|--credentials)
shift
[ $# = 0 ] && error "No credentials specified"
export CREDENTIALS="$1"
shift;;
(-b|--bandwidth)
shift
[ $# = 0 ] && error "No bandwidth specified"
export BANDWIDTH="$1"
shift;;
(-t|--timeout)
shift
[ $# = 0 ] && error "No timeout specified"
export TIMEOUT="$1"
shift;;
(-p|--port)
shift
[ $# = 0 ] && error "No port specified"
export PORT="$1"
shift;;
(-l|--compression-level)
shift
[ $# = 0 ] && error "No compression level specified"
export COMPRESS_LEVEL="$1"
shift;;
(-h|--help)
help;;
#       (-V|--version)
#           version_info;;
(*) usage;;
esac
done
}
flags "$@"
echo "source is $SOURCE"
echo "destination is $DESTINATION"
echo "credentials are $CREDENTIALS"
echo "bandwidth is $BANDWIDTH"
echo "timeout is $TIMEOUT"
echo "port is $PORT"

示例运行(脚本名称:flags53.sh(:

$ sh flags53.sh -c XYZ -d PQR -s 123 -l 4 -t 99 -b 12 -p 56789
source is 123
destination is PQR
credentials are XYZ
bandwidth is 12
timeout is 99
port is 56789
$ sh flags53.sh -c XYZ --destination PQR -s 123 -l 4 --timeout 99 -b 12 --port 56789
source is 123
destination is PQR
credentials are XYZ
bandwidth is 12
timeout is 99
port is 56789
$ sh flags53.sh -c XYZ -h
Usage: flags53 [{-s|--source} source] [{-d|--destination} destination] 
[{-c|--credentials} credentials] [{-b|--bandwidth} bandwidth] 
[{-t|--timeout} timeout] [{-p|--port} port] 
[-h|--help] [{-l|--compression-level} level]
{-s|--source} source            -- Set source directory (default: .)
{-d|--destination} destination  -- Set destination
{-c|--credentials} credentials  -- Set credentials
{-b|--bandwidth} bandwidth      -- Set maximum bandwidth
{-t|--timeout} timeout          -- Set timeout (default: 60s)
{-p|--port} port                -- Set port number (default: 1234)
{-l|--compression-level} level  -- Set compression level (default: 1)
{-h|--help}                     -- Print this help message and exit
$

请注意,请求的帮助可以转到标准输出,而不是标准错误,尽管将帮助发送到标准错误不会是严重的犯罪行为。该帮助将获取用法消息和有关每个选项含义的额外信息。注意默认值(并设置它们(也是个好主意。可能不需要导出设置——您可以在没有显式export的情况下简单地设置变量。在调用flags函数之前,或者在flags函数开始时,确实应该将变量设置为默认值。这样可以避免意外继承导出的值(环境变量(。当然,除非您想接受环境变量,但您的名称可能应该有一个适合脚本名称的系统前缀。大多数程序都应该有--version-V选项(使用-v表示"详细",而不是版本(。如果该命令不接受任何非选项(文件名(参数,请在解析循环后添加一个检查,并抱怨不需要的参数。如果命令必须至少有一个非选项参数,请改为选中。在接收--作为参数时不要报告错误;终止检查循环,并将任何剩余的参数视为非选项参数。

一个剩余的问题——函数中的移位影响函数的参数列表,而不是全局的"移位"$@&";。你必须从这个骨架中找出如何处理它。我想我可能会创建一个$OPTIND的类似物,报告要转换多少个参数才能转换到非选项参数。flags函数中的代码应该跟踪它转换了多少个参数。

这导致了修改后的代码:

#!/bin/sh
arg0=$(basename "$0" .sh)
blnk=$(echo "$arg0" | sed 's/./ /g')
usage_info()
{
echo "Usage: $arg0 [{-s|--source} source] [{-d|--destination} destination] \"
echo "       $blnk [{-c|--credentials} credentials] [{-b|--bandwidth} bandwidth] \"
echo "       $blnk [{-t|--timeout} timeout] [{-p|--port} port] \"
echo "       $blnk [-h|--help] [{-l|--compression-level} level]"
}
usage()
{
exec 1>2   # Send standard output to standard error
usage_info
exit 1
}
error()
{
echo "$arg0: $*" >&2
exit 1
}
help()
{
usage_info
echo
echo "  {-s|--source} source            -- Set source directory (default: .)"
echo "  {-d|--destination} destination  -- Set destination"
echo "  {-c|--credentials} credentials  -- Set credentials"
echo "  {-b|--bandwidth} bandwidth      -- Set maximum bandwidth"
echo "  {-t|--timeout} timeout          -- Set timeout (default: 60s)"
echo "  {-p|--port} port                -- Set port number (default: 1234)"
echo "  {-l|--compression-level} level  -- Set compression level (default: 1)"
echo "  {-h|--help}                     -- Print this help message and exit"
#   echo "  {-V|--version}                  -- Print version information and exit"
exit 0
}
flags()
{
OPTCOUNT=0
while test $# -gt 0
do
case "$1" in
(-s|--source)
shift
[ $# = 0 ] && error "No source directory specified"
export SOURCE="$1"
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-d|--destination)
shift
[ $# = 0 ] && error "No destination specified"
export DESTINATION=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-c|--credentials)
shift
[ $# = 0 ] && error "No credentials specified"
export CREDENTIALS=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-b|--bandwidth)
shift
[ $# = 0 ] && error "No bandwidth specified"
export BANDWIDTH=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-t|--timeout)
shift
[ $# = 0 ] && error "No timeout specified"
export TIMEOUT="$1"
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-p|--port)
shift
[ $# = 0 ] && error "No port specified"
export PORT=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-l|--compression-level)
shift
[ $# = 0 ] && error "No compression level specified"
export COMPRESS_LEVEL="$1"
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-h|--help)
help;;
#       (-V|--version)
#           version_info;;
(--)
shift
OPTCOUNT=$(($OPTCOUNT + 1))
break;;
(*) usage;;
esac
done
echo "DEBUG-1: [$*]" >&2
echo "OPTCOUNT=$OPTCOUNT" >&2
}
flags "$@"
echo "DEBUG-2: [$*]" >&2
echo "OPTCOUNT=$OPTCOUNT" >&2
shift $OPTCOUNT
echo "DEBUG-3: [$*]" >&2

echo "source is $SOURCE"
echo "destination is $DESTINATION"
echo "credentials are $CREDENTIALS"
echo "bandwidth is $BANDWIDTH"
echo "timeout is $TIMEOUT"
echo "port is $PORT"

如果你想做实验,还有其他写算术的方法。但不要使用expr

相关内容

最新更新