为bash脚本提供接受标志的选项,例如命令 [英] Giving a bash script the option to accepts flags, like a command

查看:46
本文介绍了为bash脚本提供接受标志的选项,例如命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个简单的bash脚本,并且希望它以任意顺序从命令行接受参数.

I'm writing a simple bash scripts and I would like it to accept parameters from the command line in any order.

我浏览了网络,并在while循环中编写了一个带有case语句的简单函数.现在,任何顺序"部分都可以工作-但它只会选择我设置的第一个参数.我当然做错了,但是脚本对我来说是很新的东西,我无法弄清楚-您的帮助将不胜感激.脚本的标志部分如下:

I've browsed around the web and wrote a simple function with a case statement in a while loop. Right now, the 'any order' part works - but it only picks up the first parameter I set. I'm certainly doing something wrong but scripting is quite new to me and I hadn't been able to figure it out - your help would be greatly appreciated. The flags part of the script is as follows:

    #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"

理想情况下,其中一些参数是必需的,而其他参数则是可选的-但这不是必须的.

Ideally, some of those parameters would be mandatory, and others optional - but that's not a must.

如何解决此脚本以任何顺序接受任何这些参数(理想情况下为长格式和短格式)?

How can I fix this script to accept any of those parameters (both long and short forms, ideally) in any order?

谢谢!

推荐答案

如注释中所述,使用参数(例如,用于凭据)后,您需要进行另一次转换.对于不存在的参数,您应该在错误报告中保持一致.如果得到 -h -help ,则只需打印帮助并退出;您不应该测试更多参数.如果需要帮助,您可以给予帮助,而无需采取其他任何措施.您还应该将错误回显为标准错误: echo"message"&& 2 .您的消息应使用脚本/程序名称作为前缀: arg0 = $(基本名称"$ 0&"; sh) echo"$ arg0:message">& 2 等.

As noted in the comments, after you consume the argument (for example for credentials), you need another shift. You should be consistent in your error reporting for non-existent arguments. If you get -h or --help, you should simply print the help and exit; you should not test for more arguments. If help is requested, you give it and do nothing else. You should also echo errors to standard error: echo "message" >&2. Your messages should be prefixed with the script/program name: arg0=$(basename "$0" .sh) and echo "$arg0: message" >&2 etc.

将更改汇总在一起,您可能会想到这样的脚本:

Putting the changes together, you might come up with a script like this:

#!/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 ):

Sample run (script name: 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 ,而不对版本使用).如果该命令不接受任何非选项(文件名)参数,请在解析循环后添加一个检查并抱怨不需要的参数.如果该命令必须至少包含一个非选项参数,请改为进行检查.接收到-作为参数时,不要报告错误;终止检查循环,并将所有剩余参数视为非选项参数.

Note that requested help can go to standard output instead of standard error, though sending the help to standard error would not be an egregious crime. The help gets the usage message and extra information about the meaning of each option. Noting defaults (and setting them) is a good idea too. It may not be necessary to export the settings — you could simply set the variables without an explicit export. You should really set the variables to their defaults before calling the flags function, or at the start of the flags function. This avoids accidentally inheriting exported values (environment variables). Unless, of course, you want to accept environment variables, but then your names should probably be given a systematic prefix appropriate for the script name. Most programs should have a --version or -V option (use -v for 'verbose', not for version). If the command does not accept any non-option (file name) arguments, add a check after the parsing loop and complain about unwanted arguments. If the command must have at least one non-option argument, check that instead. Do not report an error on receiving -- as an argument; terminate the checking loop and treat any remaining arguments as non-option arguments.

一个剩余的问题-函数的移位会影响函数的参数列表,而不是全局"$ @".您必须从该框架中找出如何处理该问题的方法.我想我可能会创建一个类似于$ OPTIND的类,该类报告要转换为非选项参数的多少个参数.flags函数中的代码应跟踪其移动了多少个参数.

One residual problem — the shifts in the function affect the function's argument list, not the global "$@". You'd have to work out how to deal with that from this skeleton. I think I'd probably create an analogue to $OPTIND that reports how many arguments to shift to get to the non-option arguments. The code in the flags function should keep track of how many arguments it shifts.

这导致修改后的代码:

#!/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 .

There are other ways of writing the arithmetic if you wish to experiment. Don't use expr though.

这篇关于为bash脚本提供接受标志的选项,例如命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆