如何在Bash中获取带有标志的参数

如何在Bash中获取带有标志的参数,bash,shell,Bash,Shell,我知道我可以很容易地在bash中获得这样的定位参数: $0或$1 我希望能够使用这样的标志选项来指定每个参数的用途: mysql -u user -h host 通过标志而不是位置获取-u参数值和-h参数值的最佳方法是什么 while test $# -gt 0; do case "$1" in -h|--help) echo "$package - attempt to capture frames" echo " " echo "$packa

我知道我可以很容易地在bash中获得这样的定位参数:

$0
$1

我希望能够使用这样的标志选项来指定每个参数的用途:

mysql -u user -h host

通过标志而不是位置获取
-u参数
值和
-h参数
值的最佳方法是什么

while test $# -gt 0; do
  case "$1" in
    -h|--help)
      echo "$package - attempt to capture frames"
      echo " "
      echo "$package [options] application [arguments]"
      echo " "
      echo "options:"
      echo "-h, --help                show brief help"
      echo "-a, --action=ACTION       specify an action to use"
      echo "-o, --output-dir=DIR      specify a directory to store output in"
      exit 0
      ;;
    -a)
      shift
      if test $# -gt 0; then
        export PROCESS=$1
      else
        echo "no process specified"
        exit 1
      fi
      shift
      ;;
    --action*)
      export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'`
      shift
      ;;
    -o)
      shift
      if test $# -gt 0; then
        export OUTPUT=$1
      else
        echo "no output dir specified"
        exit 1
      fi
      shift
      ;;
    --output-dir*)
      export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'`
      shift
      ;;
    *)
      break
      ;;
  esac
done
重点是:

  • $#
    是参数的数量
  • while循环查看提供的所有参数,并在case语句中匹配它们的值
  • 轮班把第一个带走。可以在case语句内多次移位以获取多个值

getopt是你的朋友。。一个简单的例子:

function f () {
TEMP=`getopt --long -o "u:h:" "$@"`
eval set -- "$TEMP"
while true ; do
    case "$1" in
        -u )
            user=$2
            shift 2
        ;;
        -h )
            host=$2
            shift 2
        ;;
        *)
            break
        ;;
    esac 
done;

echo "user = $user, host = $host"
}

f -u myself -h some_host

您的/usr/bin目录中应该有各种示例。

此示例使用Bash的内置命令,来自:

注意:如果一个字符后面跟一个冒号(例如,
f:
),则该选项应该有一个参数

用法示例:
/script-v-a-b-f文件名

与公认的答案相比,使用getopts有几个优点:

  • while条件更具可读性,并显示了可接受的选项
  • 清洁守则;不计算参数和移位的数量
  • 您可以加入选项(例如
    -a-b-c
    → <代码>-abc)

但是,一个很大的缺点是它不支持长选项,只支持单字符选项。

另一种选择是使用下面的示例,它允许您使用长--image或短-i标记,还允许编译-i=“example.jpg”或separate-i example.jpg传入参数的方法

# declaring a couple of associative arrays
declare -A arguments=();  
declare -A variables=();

# declaring an index integer
declare -i index=1;

# any variables you want to use here
# on the left left side is argument label or key (entered at the command line along with it's value) 
# on the right side is the variable name the value of these arguments should be mapped to.
# (the examples above show how these are being passed into this script)
variables["-gu"]="git_user";  
variables["--git-user"]="git_user";  
variables["-gb"]="git_branch";  
variables["--git-branch"]="git_branch";  
variables["-dbr"]="db_fqdn";  
variables["--db-redirect"]="db_fqdn";  
variables["-e"]="environment";  
variables["--environment"]="environment";

# $@ here represents all arguments passed in
for i in "$@"  
do  
  arguments[$index]=$i;
  prev_index="$(expr $index - 1)";

  # this if block does something akin to "where $i contains ="
  # "%=*" here strips out everything from the = to the end of the argument leaving only the label
  if [[ $i == *"="* ]]
    then argument_label=${i%=*} 
    else argument_label=${arguments[$prev_index]}
  fi

  # this if block only evaluates to true if the argument label exists in the variables array
  if [[ -n ${variables[$argument_label]} ]]
    then
        # dynamically creating variables names using declare
        # "#$argument_label=" here strips out the label leaving only the value
        if [[ $i == *"="* ]]
            then declare ${variables[$argument_label]}=${i#$argument_label=} 
            else declare ${variables[$argument_label]}=${arguments[$index]}
        fi
  fi

  index=index+1;
done;

# then you could simply use the variables like so:
echo "$git_user";

我认为这可以作为一个简单的例子来说明你想要实现的目标。不需要使用外部工具。Bash内置工具可以为您完成这项工作

function DOSOMETHING {

   while test $# -gt 0; do
           case "$1" in
                -first)
                    shift
                    first_argument=$1
                    shift
                    ;;
                -last)
                    shift
                    last_argument=$1
                    shift
                    ;;
                *)
                   echo "$1 is not a recognized flag!"
                   return 1;
                   ;;
          esac
  done  

  echo "First argument : $first_argument";
  echo "Last argument : $last_argument";
 }
这将允许您使用标志,因此无论您以何种顺序传递参数,都将获得正确的行为

例如:

 DOSOMETHING -last "Adios" -first "Hola"
输出:

 First argument : Hola
 Last argument : Adios
您可以将此函数添加到您的配置文件或将其放入脚本中

谢谢

编辑: 将此文件另存为一个文件,然后作为
yourfile.sh-最后一个“Adios”-第一个“Hola”


我在这里最喜欢Robert McMahan的答案,因为它似乎最容易生成可共享的包含文件,供您的任何脚本使用。但是它似乎有一个缺陷,行
if[[-n${variables[$argument\u label]}]
抛出消息,“variables:bad array subscript”。我没有代表发表评论,我怀疑这是正确的“修复”,但将
if
包装在
if[-n$argument_label]]中;然后
将其清理干净

这是我最后的代码,如果你知道更好的方法,请在Robert的答案中添加注释

包括文件“flags declares.sh”

包括文件“flags arguments.sh”

你的“script.sh”


如果您熟悉Python argparse,并且不介意调用Python来解析bash参数,那么我发现有一段代码非常有用并且非常易于使用,名为argparse bash

示例取自他们的Example.sh脚本:

#!/bin/bash

source $(dirname $0)/argparse.bash || exit 1
argparse "$@" <<EOF || exit 1
parser.add_argument('infile')
parser.add_argument('outfile')
parser.add_argument('-a', '--the-answer', default=42, type=int,
                    help='Pick a number [default %(default)s]')
parser.add_argument('-d', '--do-the-thing', action='store_true',
                    default=False, help='store a boolean [default %(default)s]')
parser.add_argument('-m', '--multiple', nargs='+',
                    help='multiple values allowed')
EOF

echo required infile: "$INFILE"
echo required outfile: "$OUTFILE"
echo the answer: "$THE_ANSWER"
echo -n do the thing?
if [[ $DO_THE_THING ]]; then
    echo " yes, do it"
else
    echo " no, do not do it"
fi
echo -n "arg with multiple values: "
for a in "${MULTIPLE[@]}"; do
    echo -n "[$a] "
done
echo
#/bin/bash
source$(dirname$0)/argparse.bash | |退出1

argparse“$@”我提出了一个简单的TLDR:;以联合国为例

创建一个名为greeter.sh的bash脚本

#/bin/bash
而getopts“n:”arg;做
案例$arg in
n) 名称=$OPTARG;;
以撒
完成
echo“你好$Name!”
然后,您可以在执行脚本时传递可选参数
-n

按如下方式执行脚本:

$ bash greeter.sh -n 'Bob'
输出

$ Hello Bob!
注释

如果要使用多个参数:

  • 在getops“n:”arg:do时扩展
    ,使用更多参数,例如
    
    而getops“n:o:p:”arg:do
  • 使用额外的变量分配扩展case开关。例如
    o)选项=$OPTARG
    p)参数=$OPTARG
  • 要使脚本可执行,请执行以下操作:

    chmod u+x greeter.sh
    
    另存为sample.sh 试着跑步

    sh sample.sh -n John
    

    在您的终端中。

    我在使用带有多个标志的getopts时遇到问题,所以我编写了这段代码。它使用一个模态变量来检测标志,并使用这些标志为变量分配参数

    注意,如果一个标志不应该有一个参数,那么除了设置CURRENTFLAG之外,还可以执行其他操作

        for MYFIELD in "$@"; do
    
            CHECKFIRST=`echo $MYFIELD | cut -c1`
    
            if [ "$CHECKFIRST" == "-" ]; then
                mode="flag"
            else
                mode="arg"
            fi
    
            if [ "$mode" == "flag" ]; then
                case $MYFIELD in
                    -a)
                        CURRENTFLAG="VARIABLE_A"
                        ;;
                    -b)
                        CURRENTFLAG="VARIABLE_B"
                        ;;
                    -c)
                        CURRENTFLAG="VARIABLE_C"
                        ;;
                esac
            elif [ "$mode" == "arg" ]; then
                case $CURRENTFLAG in
                    VARIABLE_A)
                        VARIABLE_A="$MYFIELD"
                        ;;
                    VARIABLE_B)
                        VARIABLE_B="$MYFIELD"
                        ;;
                    VARIABLE_C)
                        VARIABLE_C="$MYFIELD"
                        ;;
                esac
            fi
        done
    

    这就是我的解决方案。我希望能够处理不带连字符、带一个连字符和带两个连字符的布尔标志,以及带一个和两个连字符的参数/值赋值

    #处理多种类型的参数并打印一些变量
    #
    #布尔标志
    #1)没有连字符
    #create将'true'赋值给变量'create'。
    #默认值为“创建默认值”。
    #delete为变量“delete”赋值为true。
    #默认值为“删除默认值”。
    #2)一个连字符
    #a将'true'赋值给a。默认值为“false”。
    #b将'true'赋值给b。默认值为“false”。
    #3)两个连字符
    #cats将'true'指定给'cats'。默认情况下,未设置。
    #狗把“真”指定给“猫”。默认情况下,未设置。
    #
    #参数值
    #1)一个连字符
    #c分配您想要的任何值
    #d分配您想要的任何值
    #
    #2)两个连字符
    #   ... 任何事实上,无论给出什么双连字符参数,都不是
    #定义为flag,将使用其后面的下一个参数进行定义。
    #
    #例如:
    #./parser_example.sh delete-a-c VA_1--cats--dir/path/to/dir
    解析器(){
    #定义带有一个连字符的参数,这些连字符是布尔标志
    连字符标志=“a b”
    #定义带有两个连字符的参数,这两个连字符是布尔标志
    DHYPHEN_FLAGS=“猫狗”
    #迭代所有参数
    当[$#-gt 0];执行
    #处理不带连字符的参数
    如果[$1!=“-”*];
    
    $ bash greeter.sh -n 'Bob'
    
    $ Hello Bob!
    
    chmod u+x greeter.sh
    
    #!/bin/bash
    
    if getopts "n:" arg; then
      echo "Welcome $OPTARG"
    fi
    
    sh sample.sh -n John
    
        for MYFIELD in "$@"; do
    
            CHECKFIRST=`echo $MYFIELD | cut -c1`
    
            if [ "$CHECKFIRST" == "-" ]; then
                mode="flag"
            else
                mode="arg"
            fi
    
            if [ "$mode" == "flag" ]; then
                case $MYFIELD in
                    -a)
                        CURRENTFLAG="VARIABLE_A"
                        ;;
                    -b)
                        CURRENTFLAG="VARIABLE_B"
                        ;;
                    -c)
                        CURRENTFLAG="VARIABLE_C"
                        ;;
                esac
            elif [ "$mode" == "arg" ]; then
                case $CURRENTFLAG in
                    VARIABLE_A)
                        VARIABLE_A="$MYFIELD"
                        ;;
                    VARIABLE_B)
                        VARIABLE_B="$MYFIELD"
                        ;;
                    VARIABLE_C)
                        VARIABLE_C="$MYFIELD"
                        ;;
                esac
            fi
        done