如何处理Shell脚本中的开关?


17

是否有一些内置工具可以识别-x--xxxx作为开关,而不是参数,还是您必须遍历所有输入变量,测试破折号,然后解析参数?

Answers:


16

使用getopts

与POSIX规范中的一样,它非常可移植。不幸的是,它不支持长选项。

另请参阅本getopts 教程由bash-hackers Wiki提供,以及stackoverflow中的这个问题

如果仅需要简短的选项,则getopts(使用非静默错误报告)的典型用法模式是:

# process arguments "$1", "$2", ... (i.e. "$@")
while getopts "ab:" opt; do
    case $opt in
    a) aflag=true ;; # Handle -a
    b) barg=$OPTARG ;; # Handle -b argument
    \?) ;; # Handle error: unknown option or missing required argument.
    esac
done

3
应该提到的是,getopt在使用它之前,应始终将其验证为GNU getopt,但无论如何都不要使用它,因为getopts它更便于移植(并且通常更好)。如果确实由于某种原因需要调用它,请以GNU特定的方式进行调用,并确保该调用GETOPT_COMPATIBLE不在环境中。
克里斯·

结肠在while getopts "ab:" opt做什么?
user394

1
@ user394 :选项字母后表示需要一个参数。以A :作为第一个字符表示抑制错误消息。
jw013

22

我假设您正在使用bash或类似工具。一个例子:

all=false
long=false

while getopts ":hal" option; do
  case $option in
    h) echo "usage: $0 [-h] [-a] [-l] file ..."; exit ;;
    a) all=true ;;
    l) long=true ;;
    ?) echo "error: option -$OPTARG is not implemented"; exit ;;
  esac
done

# remove the options from the positional parameters
shift $(( OPTIND - 1 ))

ls_opts=()
$all && ls_opts+=( -a )
$long && ls_opts+=( -l )

# now, do it
ls "${ls_opts[@]}" "$@"

1
+1用于+=数组。不知道你能做到。真好!
James Sneeringer

5

您必须编写一个循环来解析参数。确实,您可以使用getopts命令轻松执行此操作。

这是getopts手册页中的一个简单示例:

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)    printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"

2

我最近为工作编写了一个脚本,该脚本用途广泛,可以按任意顺序进行多种开关。由于明显的法律原因,我无法透露完整的脚本(更不用说我现在不带它),但这是它的内容。您可以将其放在子例程中,然后在最后调用它您的脚本:

options () {

    if [ -n "$1" ]; then # test if any arguments passed - $1 will always exist
        while (( "$#" )); do  # process ALL arguments
            if [ "$1" = ^-t$ ]; then # -t short for "test"
                # do something here THEN shift the argument
                # shift removes it from $@ and reduces $# by
                # one if you supply no argument
                shift

            # we can also process multiple arguments at once
            elif [[ "$1" =~ ^--test=[:alnum:]{1,8}$ ]] && [[ "$2" =~ ^-t2$ ]] && [[ -n "$3" ]]; then # check for 3 arguments
                # do something more then remove them ALL from the arg list    
                shift 3
            else
                echo "No matching arguments!"
                echo "Usage: [script options list here]"
            fi
        done
    else
        echo "Usage: [script options list here]"
        exit 0
    fi
}

options "$@" # run options and loop through/process ALL arguments

我确实建议将您的bash脚本限制为少于400行/ 15k字符;我前面提到的脚本超出了这个大小,变得很难处理。我开始用Perl重写它,它更适合该任务。在bash中处理脚本时,请记住这一点。Bash非常适合小型脚本和oneliner,但任何复杂的事情都最好由Perl编写。

注意,我没有测试上面的内容,因此它可能无法正常工作,但是您可以从中得到大致的了解。


最后的调用方式options不正确,它将返回-bash: syntax error near unexpected token $@。称为options "$@"
克里斯·

是的,我混用了Perl和Bash。已更正。
laebshade 2011年

那个while条件不应该(($#))代替吗?
manatwork 2011年

为什么需要两组括号$#?编辑:你是对的。固定为while (( "$#" ))
laebshade 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.