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


13

当为以下程序编写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美元怎么办?


getopts是你想要的。否则,您可以使用带有switch语句的循环来检测每个标志(可选与否)。
Orion


@orion With getopts,我需要指定每个参数组合吗?3&4,3&5,3&4&5,等等?
山正阳'16

不,您只要设置了它们就可以设置它们,否则它会报告未找到它,因此基本上您可以按照任何顺序“获取”每个选项,并以任何顺序指定它们(如果有的话)。但是只要阅读bash手册页,就可以了。
Orion

@orion对不起,但我还是不太明白getopts。假设我强迫用户使用所有参数运行脚本:run_program.sh VAL VAL FALSE FALSE FALSE FALSE FALSE该程序以形式运行program --flag1 VAL --flag2 VAL。如果您运行run_program.sh VAL VAL FALSE 10 FALSE FALSE FALSE,该程序将以方式运行program --flag1 VAL --flag2 VAL --optflag2 10。您如何获得这种行为getopts
山正阳'16

Answers:


25

本文介绍了两种不同的方法- 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

显然,这些只是代码片段,我没有进行验证-检查是否设置了必需的args flag1和flag2等。

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


在这种情况下,我通常会做while (( $# ))而不是做错,while :;并且经常因出错而退出*
Jasen

这回答了标题,但没有回答问题。
Jasen

@Jasen我昨晚看了这个,对我的一生不知道为什么。今天早上,情况更加清晰了(我现在也看到了猎户座的答案)。我将在今天晚些时候删除此答案(我想先确认您的评论,这似乎是最简单的方法)。
约翰N

没问题,这仍然是一个很好的答案,只是这个问题已被忽略了。
杰森

1
注意:在set -o nounset第一种解决方案的情况下,如果不指定任何参数,则会出错。修复:case ${1:-} in
拉斐尔

3

使用数组。

#!/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建议了一种更好的方法。


1
您可以通过说些简化args+=( --optflag1 "$3" )。您可能希望看到对我们的参考工作的回答,即在bash / POSIX shell中不引用变量的安全隐患,我将在此处讨论该技术(通过有条件地附加可选参数在数组中构建命令行)。
G-Man说'Resstate Monica''5

@ G-Man谢谢,我没有看到文档中的内容,但了解到[@]我有足够的工具来解决我的问题。
杰森

1

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


0

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

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 != ]其中,这是语法错误,因此存在另一个任意字符来防止这种情况。这是一种非常常见的方式,您会在许多脚本中看到它。

我将OPTFLAG1它们设置""为开始时只是为了确定(以防万一之前将它们设置为某些东西),但是如果它们实际上不是在环境中声明的,那么您就不必严格执行此操作。

补充说明:

  • 实际上,您实际上可以像接收runprogram标记一样使用参数:带标志。那John N就是在说什么。那才getopts变得有用。
  • 为此目的使用FALSE有点不合标准,而且时间很长。通常,如果不是该参数的有效值,则可以使用一个字符(可能是-)来表示一个空值,或者仅传递一个空字符串(如)""。空字符串也使测试更短,只需使用即可if [ -n "$3" ]
  • 如果只有一个可选标志,或者它们是从属的,则您永远不能拥有OPTFLAG2,而没有OPTFLAG1,那么您可以简单地跳过那些不想设置的标志。如果您使用""空参数(如上述建议),则无论如何都可以跳过所有尾随的空值。

此方法几乎是将选项传递给Makefile中的编译器的方式。

再说一次-如果输入中可能包含空格,则会变得很丑陋。


不,您不希望外壳程序将参数拆分为单词。除非有充分的理由不这么做,否则您应该始终引用所有对shell变量的引用,并且您确定自己知道自己在做什么。如果您不熟悉bash / POSIX shell中的变量,请参阅对它的安全性含义,然后向下滚动到我的答案,在此我可以精确解决此问题(使用Jasen使用的相同技术)。
G-Man说'Resstate Monica''5
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.