BashShell 脚本-检查一个标志并获取它的值

我正在尝试创建一个 shell 脚本,它被设计成这样运行:

script.sh -t application

首先,在我的脚本中,我想检查脚本是否已经使用-t 标志运行。例如,如果它已经运行没有这样的标志,我希望它的错误:

script.sh

其次,假设有一个-t 标志,我想获取这个值并将其存储在一个变量中,我可以在脚本中使用这个变量,例如:

FLAG="application"

到目前为止,我在这方面取得的唯一进展是 $@ 获取了所有命令行参数,但我不知道这与标志有什么关系,也不知道这是否可能。

191868 次浏览

You should read this getopts tutorial.

Example with -a switch that requires an argument :

#!/bin/bash


while getopts ":a:" opt; do
case $opt in
a)
echo "-a was triggered, Parameter: $OPTARG" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done

Like greybot said(getopt != getopts) :

The external command getopt(1) is never safe to use, unless you know it is GNU getopt, you call it in a GNU-specific way, and you ensure that GETOPT_COMPATIBLE is not in the environment. Use getopts (shell builtin) instead, or simply loop over the positional parameters.

Use $# to grab the number of arguments, if it is unequal to 2 there are not enough arguments provided:

if [ $# -ne 2 ]; then
usage;
fi

Next, check if $1 equals -t, otherwise an unknown flag was used:

if [ "$1" != "-t" ]; then
usage;
fi

Finally store $2 in FLAG:

FLAG=$2

Note: usage() is some function showing the syntax. For example:

function usage {
cat << EOF
Usage: script.sh -t <application>


Performs some activity
EOF
exit 1
}

Try shFlags -- Advanced command-line flag library for Unix shell scripts.

https://github.com/kward/shflags

It is very good and very flexible.

FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All flags take a name, default value, help-string, and optional 'short' name (one-letter name). Some flags have other arguments, which are described with the flag.

DEFINE_string: takes any input, and intreprets it as a string.

DEFINE_boolean: typically does not take any argument: say --myflag to set FLAGS_myflag to true, or --nomyflag to set FLAGS_myflag to false. Alternately, you can say --myflag=true or --myflag=t or --myflag=0 or --myflag=false or --myflag=f or --myflag=1 Passing an option has the same affect as passing the option once.

DEFINE_float: takes an input and intreprets it as a floating point number. As shell does not support floats per-se, the input is merely validated as being a valid floating point value.

DEFINE_integer: takes an input and intreprets it as an integer.

SPECIAL FLAGS: There are a few flags that have special meaning: --help (or -?) prints a list of all the flags in a human-readable fashion --flagfile=foo read flags from foo. (not implemented yet) -- as in getopt(), terminates flag-processing

EXAMPLE USAGE:

-- begin hello.sh --
! /bin/sh
. ./shflags
DEFINE_string name 'world' "somebody's name" n
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"
echo "Hello, ${FLAGS_name}."
-- end hello.sh --


$ ./hello.sh -n Kate
Hello, Kate.

Note: I took this text from shflags documentation

Here is a generalized simple command argument interface you can paste to the top of all your scripts.

#!/bin/bash


declare -A flags
declare -A booleans
args=()


while [ "$1" ];
do
arg=$1
if [ "${1:0:1}" == "-" ]
then
shift
rev=$(echo "$arg" | rev)
if [ -z "$1" ] || [ "${1:0:1}" == "-" ] || [ "${rev:0:1}" == ":" ]
then
bool=$(echo ${arg:1} | sed s/://g)
booleans[$bool]=true
echo \"$bool\" is boolean
else
value=$1
flags[${arg:1}]=$value
shift
echo \"$arg\" is flag with value \"$value\"
fi
else
args+=("$arg")
shift
echo \"$arg\" is an arg
fi
done




echo -e "\n"
echo booleans: ${booleans[@]}
echo flags: ${flags[@]}
echo args: ${args[@]}


echo -e "\nBoolean types:\n\tPrecedes Flag(pf): ${booleans[pf]}\n\tFinal Arg(f): ${booleans[f]}\n\tColon Terminated(Ct): ${booleans[Ct]}\n\tNot Mentioned(nm): ${boolean[nm]}"
echo -e "\nFlag: myFlag => ${flags["myFlag"]}"
echo -e "\nArgs: one: ${args[0]}, two: ${args[1]}, three: ${args[2]}"

By running the command:

bashScript.sh firstArg -pf -myFlag "my flag value" secondArg -Ct: thirdArg -f

The output will be this:

"firstArg" is an arg
"pf" is boolean
"-myFlag" is flag with value "my flag value"
"secondArg" is an arg
"Ct" is boolean
"thirdArg" is an arg
"f" is boolean




booleans: true true true
flags: my flag value
args: firstArg secondArg thirdArg


Boolean types:
Precedes Flag(pf): true
Final Arg(f): true
Colon Terminated(Ct): true
Not Mentioned(nm):


Flag: myFlag => my flag value


Args: one => firstArg, two => secondArg, three => thirdArg

Basically, the arguments are divided up into flags booleans and generic arguments. By doing it this way a user can put the flags and booleans anywhere as long as he/she keeps the generic arguments (if there are any) in the specified order.

Allowing me and now you to never deal with bash argument parsing again!

You can view an updated script here

This has been enormously useful over the last year. It can now simulate scope by prefixing the variables with a scope parameter.

Just call the script like

replace() (
source $FUTIL_REL_DIR/commandParser.sh -scope ${FUNCNAME[0]} "$@"
echo ${replaceFlags[f]}
echo ${replaceBooleans[b]}
)

Doesn't look like I implemented argument scope, not sure why I guess I haven't needed it yet.