如果没有给出顺序,如何解析 bash 脚本中的可选参数?

如果没有给出顺序,如何解析 bash 脚本中的可选参数?

我很困惑在为以下程序编写 bash 脚本时如何包含可选参数/标志:

该程序需要两个参数:

run_program --flag1 <value> --flag2 <value>

但是,有几个可选标志:

run_program --flag1 <value> --flag2 <value> --optflag1 <value> --optflag2 <value> --optflag3 <value> --optflag4 <value> --optflag5 <value> 

我想运行 bash 脚本,以便它接受用户参数。如果用户只按顺序输入两个参数,那么它将是:

#!/bin/sh

run_program --flag1 $1 --flag2 $2

但是如果包含任何可选参数怎么办?我认为会是

if [ --optflag1 "$3" ]; then
    run_program --flag1 $1 --flag2 $2 --optflag1 $3
fi

但是如果给了 4 美元而不是 3 美元怎么办?

答案1

本文显示了两种不同的方法 -shiftgetopts(并讨论了两种方法的优点和缺点)。

使用shift您的脚本查看$1,决定采取什么操作,然后执行shift、移动$2$1$3$2等。

例如:

while :; do
    case $1 in
        -a|--flag1) flag1="SET"            
        ;;
        -b|--flag2) flag2="SET"            
        ;;
        -c|--optflag1) optflag1="SET"            
        ;;
        -d|--optflag2) optflag2="SET"            
        ;;
        -e|--optflag3) optflag3="SET"            
        ;;
        *) break
    esac
    shift
done

您可以在表达式getopts中定义(短)选项while

while getopts abcde opt; do
    case $opt in
        a) flag1="SET"
        ;;
        b) flag2="SET"
        ;;
        c) optflag1="SET"
        ;;
        d) optflag2="SET"
        ;;
        e) optflag3="SET"
        ;;
    esac
done

显然,这些只是代码片段,我省略了验证 - 检查是否设置了强制参数 flag1 和 flag2 等。

您使用哪种方法在某种程度上取决于您的喜好 - 您希望脚本的可移植性如何,您是否只能接受短(POSIX)选项,或者是否需要长(GNU)选项,等等。

答案2

使用数组。

#!/bin/bash

args=( --flag1 "$1" --flag2 "$2" )
[  "x$3" = xFALSE ] ||  args+=( --optflag1 "$3" )
[  "x$4" = xFALSE ] ||  args+=( --optflag2 "$4" )
[  "x$5" = xFALSE ] ||  args+=( --optflag3 "$5" )
[  "x$6" = xFALSE ] ||  args+=( --optflag4 "$6" )
[  "x$7" = xFALSE ] ||  args+=( --optflag5 "$7" )

program_name "${args[@]}"

这将正确处理其中包含空格的参数。

[编辑] 我使用的是大致等效的语法,args=( "${args[@]}" --optflag1 "$3" )但 G-Man 提出了更好的方法。

答案3

在 shell 脚本中,参数为“$1”、“$2”、“$3”等。参数的数量为 $#。
如果您的脚本无法识别选项,您可以忽略选项检测并将所有参数视为操作数。
要识别选项,请使用内置的 getopts

答案4

如果你的输入选项是位置性的(你知道它们在哪里),并且没有用标志指定,那么你想要的只是构建命令行。只需为所有这些准备命令参数:

FLAG1="--flag1 $1"
FLAG2="--flag2 $2"
OPTFLAG1=""
OPTFLAG2=""
OPTFLAG3=""
if [ xFALSE != x"$3" ]; then
   OPTFLAG1="--optflag1 $3"
fi
if [ xFALSE != x"$4" ]; then
   OPTFLAG2="--optflag2 $4"
fi
#the same for other flags

#now just run the program
runprogram $FLAG1 $FLAG2 $OPTFLAG1 $OPTFLAG2 $OPTFLAG3

如果未指定参数,则相应的字符串为空并展开为空。请注意,最后一行中没有引号。这是因为您希望 shell 将参数拆分为单词(将--flag1$1作为程序的单独参数)。当然,如果你的原始参数包含空格,这就会出错。如果您是运行此脚本的人,那么您可以保留它,但如果这是一个通用脚本,如果用户输入带有空格的内容,它可能会出现意外的行为。为了解决这个问题,您需要使代码变得更丑一些。

x测试中的前缀存在[]以防万一$3$4为空。在这种情况下,bash 会扩展[ FALSE != $3 ][ FALSE != ]which is 语法错误,因此可以使用另一个任意字符来防止这种情况。这是一种非常常见的方式,您会在许多脚本中看到它。

我将OPTFLAG1它们和其余的设置""在开始只是为了确定(以防它们之前设置为某些内容),但如果它们实际上没有在环境中声明,那么您并不严格需要这样做。

一些补充说明:

  • 实际上,您可以按照与以下相同的方式接收参数runprogram:使用标志。这就是我们John N所说的。这就是getopts有用的地方。
  • 使用 FALSE 来达到这个目的有点不标准而且很长。通常,人们会使用单个字符(可能-)来表示空值,或者仅传递一个空字符串,例如"",如果这不是参数的有效值。空字符串也会使测试更短,只需使用if [ -n "$3" ].
  • 如果只有一个可选标志,或者它们是相关的,因此您永远不能有 OPTFLAG2,但不能有 OPTFLAG1,那么您可以简单地跳过您不想设置的标志。如果您使用""空参数,就像上面建议的那样,您无论如何都可以跳过所有尾随的空参数。

这种方法几乎就是在 Makefile 中将选项传递给编译器的方式。

再说一次 - 如果输入可能包含空格,它会变得很难看。

相关内容