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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7069682/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:53:51  来源:igfitidea点击:

How to get arguments with flags in Bash

bashshell

提问by Stann

I know that I can easily get positioned parameters like this in bash:

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

$0or $1

$0或者 $1

I want to be able to use flag options like this to specify for what each parameter is used:

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

mysql -u user -h host

What is the best way to get -u paramvalue and -h paramvalue by flag instead of by position?

按标志而不是按位置获取-u param价值和-h param价值的最佳方法是什么?

回答by Dennis

This example uses Bash's built-in getoptscommand and is from the Google Shell Style Guide:

此示例使用 Bash 的内置getopts命令,并且来自Google Shell 样式指南

a_flag=''
b_flag=''
files=''
verbose='false'

print_usage() {
  printf "Usage: ..."
}

while getopts 'abf:v' flag; do
  case "${flag}" in
    a) a_flag='true' ;;
    b) b_flag='true' ;;
    f) files="${OPTARG}" ;;
    v) verbose='true' ;;
    *) print_usage
       exit 1 ;;
  esac
done

Note: If a character is followed by a colon (e.g. f:), that option is expected to have an argument.

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

Example usage: ./script -v -a -b -f filename

用法示例: ./script -v -a -b -f filename

Using getopts has several advantages over the accepted answer:

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

  • the while condition is a lot more readable and shows what the accepted options are
  • cleaner code; no counting the number of parameters and shifting
  • you can join options (e.g. -a -b -c-abc)
  • while 条件更具可读性,并显示了可接受的选项是什么
  • 更干净的代码;不计算参数的数量和移位
  • 你可以加入选项(例如-a -b -c-abc

However, a big disadvantage is that it doesn't support long options, only single-character options.

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

回答by Flexo

This is the idiom I usually use:

这是我通常使用的成语:

while test $# -gt 0; do
  case "" 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=
      else
        echo "no process specified"
        exit 1
      fi
      shift
      ;;
    --action*)
      export PROCESS=`echo  | sed -e 's/^[^=]*=//g'`
      shift
      ;;
    -o)
      shift
      if test $# -gt 0; then
        export OUTPUT=
      else
        echo "no output dir specified"
        exit 1
      fi
      shift
      ;;
    --output-dir*)
      export OUTPUT=`echo  | sed -e 's/^[^=]*=//g'`
      shift
      ;;
    *)
      break
      ;;
  esac
done

Key points are:

关键点是:

  • $#is the number of arguments
  • while loop looks at all the arguments supplied, matching on their values inside a case statement
  • shift takes the first one away. You can shift multiple times inside of a case statement to take multiple values.
  • $#是参数的数量
  • while 循环查看提供的所有参数,匹配它们在 case 语句中的值
  • shift 带走了第一个。您可以在 case 语句内多次移动以获取多个值。

回答by Shizzmo

getopt is your friend.. a simple example:

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

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

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

f -u myself -h some_host

There should be various examples in your /usr/bin directory.

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

回答by Matias Barrios

I think this would serve as a simpler example of what you want to achieve. There is no need to use external tools. Bash built in tools can do the job for you.

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

function DOSOMETHING {

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

  echo "First argument : $first_argument";
  echo "Last argument : $last_argument";
 }

This will allow you to use flags so no matter which order you are passing the parameters you will get the proper behavior.

这将允许您使用标志,因此无论您传递参数的顺序如何,您都将获得正确的行为。

Example :

例子 :

 DOSOMETHING -last "Adios" -first "Hola"

Output :

输出 :

 First argument : Hola
 Last argument : Adios

You can add this function to your profile or put it inside of a script.

您可以将此功能添加到您的配置文件中或将其放入脚本中。

Thanks!

谢谢!

Edit : Save this as a a file and then execute it as yourfile.sh -last "Adios" -first "Hola"

编辑:将其另存为 aa 文件,然后将其执行为 yourfile.sh -last "Adios" -first "Hola"

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

  echo "First argument : $first_argument";
  echo "Last argument : $last_argument";

回答by Robert McMahan

Another alternative would be to use something like the below example which would allow you to use long --imageor short -itags and also allow compiled -i="example.jpg"or separate -i example.jpgmethods of passing in arguments.

另一种选择是使用类似于下面的示例的内容,它允许您使用长--image或短-i标签,并允许编译-i="example.jpg"或单独的-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";

回答by Michael

I like Robert McMahan's answer the best here as it seems the easiest to make into sharable include files for any of your scripts to use. But it seems to have a flaw with the line if [[ -n ${variables[$argument_label]} ]]throwing the message, "variables: bad array subscript". I don't have the rep to comment, and I doubt this is the proper 'fix,' but wrapping that ifin if [[ -n $argument_label ]] ; thencleans it up.

我最喜欢 Robert McMahan 的回答,因为它似乎最容易制作成可共享的包含文件,供您使用的任何脚本使用。但是,if [[ -n ${variables[$argument_label]} ]]抛出消息“变量:数组下标错误”的行似乎存在缺陷。我没有代表来评论,我怀疑这是正确的“修复”,但包装是ifif [[ -n $argument_label ]] ; then清除它。

Here's the code I ended up with, if you know a better way please add a comment to Robert's answer.

这是我最终得到的代码,如果您知道更好的方法,请在罗伯特的回答中添加评论。

Include File "flags-declares.sh"

包含文件“flags-declares.sh”

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

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

Include File "flags-arguments.sh"

包含文件“flags-arguments.sh”

# $@ 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

  if [[ -n $argument_label ]] ; then
    # 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
  fi

  index=index+1;
done;

Your "script.sh"

你的“script.sh”

. bin/includes/flags-declares.sh

# 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";

. bin/includes/flags-arguments.sh

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

回答by Linh

If you're familiar with Python argparse, and don't mind calling python to parse bash arguments, there is a piece of code I found really helpful and super easy to use called argparse-bash https://github.com/nhoffman/argparse-bash

如果您熟悉 Python argparse,并且不介意调用 python 来解析 bash 参数,我发现有一段代码非常有用且非常易于使用,称为 argparse-bash https://github.com/nhoffman/ argparse-bash

Example take from their example.sh script:

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

#!/bin/bash

source $(dirname 
#!/bin/bash

while getopts "n:" arg; do
  case $arg in
    n) Name=$OPTARG;;
  esac
done

echo "Hello $Name!"
)/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

回答by pijemcolu

I propose a simple TLDR:; example for the un-initiated.

我提出了一个简单的 TLDR:; 未启动的示例。

Create a bash script called helloworld.sh

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

$ bash helloworld.sh -n 'World'

You can then pass an optional parameter -nwhen executing the script.

然后,您可以-n在执行脚本时传递一个可选参数。

Execute the script as such:

执行脚本如下:

$ Hello World!

Output

输出

#!/bin/bash

if getopts "n:" arg; then
  echo "Welcome $OPTARG"
fi

Notes

笔记

If you'd like to use multiple parameters:

如果您想使用多个参数:

  1. extend while getops "n:" arg: dowith more paramaters such as while getops "n:o:p:" arg: do
  2. extend the case switch with extra variable assignments. Such as o) Option=$OPTARGand p) Parameter=$OPTARG
  1. 扩展while getops "n:" arg: do更多参数,例如 while getops "n:o:p:" arg: do
  2. 使用额外的变量赋值扩展 case 开关。比如o) Option=$OPTARGp) Parameter=$OPTARG

回答by Nishant Ingle

sh sample.sh -n John

Save it as sample.sh and try running

将其另存为 sample.sh 并尝试运行

    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

in your terminal.

在您的终端中。

回答by Jessica Richards

I had trouble using getopts with multiple flags, so I wrote this code. It uses a modal variable to detect flags, and to use those flags to assign arguments to variables.

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

Note that, if a flag shouldn't have an argument, something other than setting CURRENTFLAG can be done.

请注意,如果标志不应该有参数,则可以执行除设置 CURRENTFLAG 以外的其他操作。

##代码##