如何在bash中分离选项和参数



我的脚本需要两个参数才能运行。

如果需要的话,它可以有选项,所以我想确保这些属性,所以我写了以下脚本

 if [ $# -lt 3 ]
    then
     echo "This command need 3 arguments"
    else
     echo $1 $2 $3
  fi

观察到"$#"统计传递的参数总数,包括选项

OUTPUT :
sh my_script.sh -o optional1 -r.

它起作用,但由于我的检查失败,所以没有达到预期效果。

Expected Output :(two arguments are compulsory)
sh my_script.sh arg1 arg2 --> should work
sh my_script.sh -o optional1 -r optional2 --> throw error that it requires two arguments
sh my_script.sh -o optional1 -r optional2 arg1 arg2 --> should work

没有getopts,但基本相同,我更喜欢这种方式,因为添加长args更容易。

#!/bin/bash

while true;do
        case $1 in
        '-o'|'--optional')
                optional1=$2
                shift 2
                ;;
        '-r')
                optional2=$2
                shift 2
                ;;
        *)
                break
                ;;
        esac
done
if [[ $# -lt 2 ]];then
    echo Too few args.
    exit 1
fi
echo $optional1
echo $optional2

显然,它可能更健壮,因为这将使用多个相同的标志。

  1. 使用getopts处理选项。

    r_flag=0
    o_flag="default value"
    while getopts o:r arg
    do
        case "$arg" in
        (o) o_flag="$OPTARG";;
        (r) r_flag=1;;
        (*) echo "Unrecognized option $arg" >&2; exit 1;;
        esac
    done
    
  2. 循环完成后,使用shift $(($OPTIND - 1))来消除已处理的选项。

  3. 现在您可以检查是否至少还有2个参数:

    if [ $# -lt 2 ]
    then
        echo "Usage: $(basename $0 .sh) [-o option][-r] arg1 arg2 ..." >&2
        exit 1
    fi
    

注意$#报告参数数positional parameters)。它不在乎它们是什么。命令行上的任何内容都被视为参数。(例如somevar-o)。因此,要区分-foofoo,您需要编写一个解释器,或者使用其他人提供的getopts(作为该解释器)

虽然getopts很好,但我通常需要自己的翻译。为了在sh中提供自定义解释器,您通常会使用while loopshiftcase语句来检查每个位置参数并做出相应的响应。虽然与bash相比,sh中的字符串操作有些有限,但这已经足够了。

一个符合您要求的例子可能是:

#!/bin/sh
if [ $# -lt 2 ]; then
    printf "This command need 2 argumentsn"
    exit 1
fi
let nargs=0
while [ "$1" != "" ]; do
    printf " processing: %sn" "$1"
    case "$1" in
        -r  )   shift
                [ "$1" = "" -o  `expr index "$1" "-"` -eq 1 ] && 
                { printf "error: -r requires an option.n"; exit 1; }
                r_var="$1"
                printf "   -r %sn" "$r_var"
                let nargs=$nargs+1
                ;;
        -*  )   ;;  ## don't count '-x' arguments as options (change as needed)
        *   )   let nargs=$nargs+1
                ;;
    esac
    shift
done
if [ $nargs -lt 2 ]; then
    printf "n Two compulsory arguments not given.nn"
    exit 1
else
    printf "n required arguments satisfied.nn"
fi

exit 0

示例使用/输出

$ sh params.sh arg1 arg2
 processing: arg1
 processing: arg2
 required arguments satisfied.

$ sh params.sh -o optional -r
 processing: -o
 processing: optional
 processing: -r
error: -r requires an option.

$ sh params.sh -o optional -r arg1 arg2
 processing: -o
 processing: optional
 processing: -r
   -r arg1
 processing: arg2
 required arguments satisfied.

$ sh params.sh -o arg2
 processing: -o
 processing: arg2
 Two compulsory arguments not given.

检查重复参数

如果您不想尝试覆盖case语句中的所有内容,那么检查位置参数是否重复的嵌套for循环可能是检查重复参数的最佳方式。一种方法是在脚本的开头进行检查:

#!/bin/sh
let args=2
## check duplicate arguments
for i ; do
    for j in $(seq $args $#); do
        printf "i: %s  j: %s compare %s %sn" $i $j $i ${!j}  ## delete - just for info
        if [ $i = ${!j} ]; then
            printf "error: duplicate args [ %s = %s ], exiting.n" $i ${!j}
            exit 1;
        fi
    done
    let args=$args+1
done
exit 0

示例/输出

$ sh pdupes.sh a b c d e
i: a  j: 2 compare a b
i: a  j: 3 compare a c
i: a  j: 4 compare a d
i: a  j: 5 compare a e
i: b  j: 3 compare b c
i: b  j: 4 compare b d
i: b  j: 5 compare b e
i: c  j: 4 compare c d
i: c  j: 5 compare c e
i: d  j: 5 compare d e
$ sh pdupes.sh a b c d b e
i: a  j: 2 compare a b
i: a  j: 3 compare a c
i: a  j: 4 compare a d
i: a  j: 5 compare a b
i: a  j: 6 compare a e
i: b  j: 3 compare b c
i: b  j: 4 compare b d
i: b  j: 5 compare b b
error: duplicate args [ b = b ], exiting.

防止两次接受争论

除了上面的循环检查将捕获任何重复项之外,您还可以向case语句添加一个选项,以防止两次接受参数。这取决于你是想跳过重复还是标记它并退出。在带有可选参数的-o的情况下,我会尝试下面的方法,它接受一个可选参数,测试它不是另一个-x类型,并检查它是否已经设置。如果它通过了所有测试,就设置了`o_var,通过设置,它就不能再设置了:

    -o  )   shift
            [ "$1" = "" -o  `expr index "$1" "-"` -eq 1 -o ! -z $o_var ] && 
            { printf "duplicate value for '-o' received, exitingn"; exit 1; } || 
            o_var="$1" && let nargs=$nargs+1
            printf "   -o %sn" "$o_var"
            ;;

轻微变化控制对重复的的响应

有两种额外的方式来处理多次尝试设置CCD_ 22。第一种是在参数列表中第二次出现-o时退出(无论是否尝试设置与-o相关的可选变量):

    -o  )   shift
            ## throw error on any second instance of '-o' 
            if [ -z "$o_var" ]; then
                if [ ! `expr index "$1" "-"` -eq 1 -a "$1" != "" ]; then
                    o_var="$1"
                    let nargs=$nargs+1
                    printf "   -o %sn" "$o_var"
                fi
            else
                    printf "error: multiple attempts to set '-o', exiting.n" "$o_var" "$1"
                    exit 1                
            fi
            ;;

示例

两次尝试再次设置-o的处理方式相同:

$ sh params.sh -o foo -r arg1 arg2 -o bar
$ sh params.sh -o foo -r arg1 arg2 -o
 processing: -o
   -o foo
 processing: -r
   -r arg1
 processing: arg2
 processing: -o
error: multiple attempts to set '-o', exiting.

另一种可能性是,只有在第二次尝试设置与-o相关联的可选变量时,才抛出错误。(例如,如果第二个-o是无害的,则忽略它):

    -o  )   shift
            ## throw error only if second instance of '-o' has optional argument
            if [ "$1" != "" -a ! `expr index "$1" "-"` -eq 1 ]; then
                if [ -z "$o_var" ]; then
                    o_var="$1"
                    let nargs=$nargs+1
                    printf "   -o %sn" "$o_var"
                else
                    printf "error: multiple attempts to set '-o'. (%s, now '%s')n" "$o_var" "$1"
                    exit 1
                fi
            fi
            ;;

示例

$ sh params.sh -o foo -r arg1 arg2 -o bar
 processing: -o
   -o foo
 processing: -r
   -r arg1
 processing: arg2
 processing: -o
error: multiple attempts to set '-o'. (foo, now 'bar')
$ sh params.sh -o foo -r arg1 arg2 -o
 processing: -o
   -o foo
 processing: -r
   -r arg1
 processing: arg2
 processing: -o
 required arguments satisfied.

最新更新