如何在 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
How to get arguments with flags in Bash
提问by Stann
I know that I can easily get positioned parameters like this in bash:
我知道我可以很容易地在 bash 中获得这样的定位参数:
$0
or $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 param
value and -h param
value by flag instead of by position?
按标志而不是按位置获取-u param
价值和-h param
价值的最佳方法是什么?
回答by Dennis
This example uses Bash's built-in getopts
command 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 if
in if [[ -n $argument_label ]] ; then
cleans it up.
我最喜欢 Robert McMahan 的回答,因为它似乎最容易制作成可共享的包含文件,供您使用的任何脚本使用。但是,if [[ -n ${variables[$argument_label]} ]]
抛出消息“变量:数组下标错误”的行似乎存在缺陷。我没有代表来评论,我怀疑这是正确的“修复”,但包装是if
在if [[ -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 -n
when 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:
如果您想使用多个参数:
- extend
while getops "n:" arg: do
with more paramaters such aswhile getops "n:o:p:" arg: do
- extend the case switch with extra variable assignments. Such as
o) Option=$OPTARG
andp) Parameter=$OPTARG
- 扩展
while getops "n:" arg: do
更多参数,例如while getops "n:o:p:" arg: do
- 使用额外的变量赋值扩展 case 开关。比如
o) Option=$OPTARG
和p) 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 以外的其他操作。
##代码##