We often encounter scenarios in which command line parameters need to be obtained in Shell scripts. The easiest way is to obtain them through $1
, $2
……. This method is simple and practical for scenarios that only require one or two parameters, but it will cause some problems for more complex scenarios that require multiple parameters and optional parameters:
- The order of parameter input must be strictly followed, which is not easy to remember and error-prone
- Unable to provide “parameter default value”
- The meaning of the parameters is not clear enough
As an example, suppose we have a script to connect to the host, and the following parameters need to be passed in:
- username
- password
- Host (required, not optional, no need to specify the parameter name)
- port
- Show connection details
If the parameters are obtained through $1
and $2
, the problems raised above will become more obvious.
We prefer to pass parameters in a way similar to the following:
$ myscript -u username -p password -v -n 9999 192.168.1.2
This is where getopt
/ getopts
comes into play.
getopts
vs getopt
Both getopt
and getopts
are tools used in Bash to obtain and analyze command line parameters, and are often used in shell scripts to analyze script parameters.
getopts
is a shell builtin andgetopt
is a standalone external toolgetopts
uses a simple syntax, andgetopt
uses a more complex syntaxgetopts
does not support long parameters (eg:--option
),getopt
supports long parameters- The purpose of
getopts
is to replacegetopt
in less complex scenarios to perform parameter analysis more quickly getopts
is responsible for parameter parsing, which can easily extract parameter values,getopt
is only responsible for rearranging parameters according to the rules, further analysis needs to be written by yourself
getopts
usage instructions
1. Example
According to the aforementioned case:
$ myscript.sh -u username -p password -v -n 8888 127.0.0.1
Parameter Description:
parameter | description |
---|---|
-u | username |
-p | password |
-n | port |
-v | show details |
no name parameter | host |
#!/bin/bash
# Handle script parameters
# -u username
# -p password
# -v Whether to display details
# -n port
while getopts ":u:p:n:v" opt_name # Through the loop, use getopts to parse according to the specified parameter list, and store the parameter name in opt_name
do
case "$opt_name" in # Judging the processing branch according to the parameter name
'u') # -u
CONN_USERNAME="$OPTARG" # get parameter value from $OPTARG
;;
'p') # -p
CONN_PASSWORD="$OPTARG"
;;
'v') # -v
CONN_SHOW_DETAIL=true
;;
'n') # -n
CONN_PORT="$OPTARG"
;;
?) # other unspecified name parameters
echo "Unknown argument(s)."
exit 2
;;
esac
done
# remove parsed parameters
shift $((OPTIND-1))
# Get the host through the first unnamed parameter
CONN_HOST="$1"
# Display the result of getting parameters
echo username "$CONN_USERNAME"
echo password "$CONN_PASSWORD"
echo host "$CONN_HOST"
echo port "$CONN_PORT"
echo show details "$CONN_SHOW_DETAIL"
2. Description
The way getopts
works is:
(1) Specify the parameter name that needs to be parsed in the command line
In this example, :u:p:n:v
is to specify the name of the parameter to be parsed.
Rule description:
- The letters in it represent the parameter names that need to be parsed
- The colon
:
after the letter means that in addition to itself, the parameter will also take a parameter as the value of the option, and the value passed in is obtained through the$OPTARG
variable - There is no colon
:
after the letter, indicating that the parameter is a switch type option, no need to specify a value, it is only used as a mark of existence - The colon
:
at the beginning of the string indicates that during the parsing process, an error message will not be displayed if a parameter that is not specified in thegetopts
parameter list is encountered. Otherwise an error will be reported.
(2) Read the parameters one by one through the loop
When using getopts
to parse parameters, it will be parsed in order according to the specified parameter list. If this parsing conforms to the specified parameter rules, including the parameter name, whether a value needs to be passed, etc., it will return success, and continue parsing in the next cycle, otherwise exit the cycle.
Failure rules:
- encountered an undefined variable
- Encountered an unexpected value, such as: specifying a parameter after a parameter that does not need to pass a value, or passing in more values than expected
Exit the loop on failure.
Notice! ! ! Parameters without names must be written at the end! Otherwise, it will be regarded as an unexpected parameter, causing the parsing to stop.
(3) After parsing all expected parameters, process the remaining parameters
After parsing all expected parameters (the loop will exit at this time), the variable $OPTIND
stores the Index of the last parsed parameter. If there are other parameters, it is considered to be an unexpected parameter of getopts
. At this time, you can cooperate with shift
to clear the parsed parameters, and get the remaining parameters through $1
and $2
.
shift $((OPTIND-1))
3. Limitations of getopts
For commonly used and less complicated scenarios, using getopts
to process parameters is basically sufficient and more convenient, and it is an internal command, which does not need to consider installation issues, but there are also some limitations:
- The format of the option parameter must be
-d val
instead of-dval
without spaces - All
option parameters
must be written in front of other parameters, becausegetopts
starts processing from the front of the command line, and it will stop when it encounters a parameter that does not start with-
, or the option parameter end tag--
, if If you encounternon-option command line parameters
in the middle, you will not be able to get the following option parameters. - long options like
--debug
are not supported
/usr/share/doc/util-linux-2.23.2
/usr/share/getopt/
/usr/share/docs/
This example refers to the following script:
/usr/share/doc/util-linux-2.23.2/getopt-parse.bash
The built-in getopt
function of macOS is relatively weak and does not support long options. You can install the GNU version gnu-getopt
:
$ brew install gnu-getopt
2. Introduction
The help information is as follows:
$ getopt --help
usage:
getopt optstring parameters
getopt [options] [--] optstring parameters
getopt [options] -o|--options optstring [options] [--] parameters
options:
-a, --alternative allow long options to start with -
-h, --help this short usage guide
-l, --longoptions <long options> long options to recognize
-n, --name <program name> program name to report errors to
-o, --options <options string> Short options to recognize
-q, --quiet suppress error reporting by getopt(3)
-Q, --quiet-output no quiet output
-s, --shell <shell> set shell quoting rules
-T, --test test getopt(1) version
-u, --unquoted unquote output
-V, --version output version information
3. Examples
According to the case mentioned above, here is a “log level” option, which has a default value, and you can also specify the parameter value yourself.
# short parameter format
$ myscript -u username -p password -v -n 9999 192.168.1.2 -l3
# or long parameter format
$ myscript --username username --password password --verbose --port 9999 192.168.1.2 --log-level=3
Parameter Description:
parameter | description |
---|---|
-u , --username | username |
-p , --password | password |
-n , --port | port |
-v , --verbose | show details |
-l , --log-level | log level, the default level is 1 |
no name parameter | host |
#!/bin/bash
# Use `"$@"' to have each command-line argument expand to a separate word. The quotes around `$@' are essential!
# Use getopt to tidy up parameters
ARGS=$(getopt -o 'u:p:n:vl::' -l 'username:,password:,port:,verbose,log-level::' -- "$@")
if [ $? != 0 ] ; then echo "Parse error! Terminating..." >&2 ; exit 1 ;
# Set the parameters to getopt's sorted parameters
# $ARGS needs to be surrounded by quotes
eval set -- "$ARGS"
# loop parsing parameters
while true ;
# start parsing from the first parameter
case "$1" in
# Username needs to have parameter value, so get the parameter value through $2, after getting it, use shift to clear the obtained parameters
-u|--username) CONN_USERNAME="$2" ; shift 2 ;;
# password, the acquisition rules are the same as above
-p|--password) CONN_PASSWORD="$2" ; shift 2 ;;
# Port, get the same rules as above
-n|--port) CONN_PORT="$2" ; shift 2 ;;
# Whether to display details, switch parameters, with this option, this branch will be executed
-v|--verbose) CONN_SHOW_DETAIL=true ; shift ;;
# log level, default parameter
# Short form: -l3
# Long format: --log-level=3
-l|--log-level)
# If the parameter item is specified, but the parameter value is not specified, an empty string will be obtained by default, and the default value can be used according to this rule
# If a parameter value is specified, use the parameter value
case "$2" in
"") CONN_LOG_LEVEL=1 ; shift 2 ;;
*) CONN_LOG_LEVEL="$2" ; shift 2 ;;
esac;;
--) shift ; break ;;
*) echo "Internal error!" ; exit 1 ;;
esac
done
# Get the host through the first unnamed parameter
CONN_HOST="$1"
# Display the result of getting parameters
echo 'Username: ' "$CONN_USERNAME"
echo 'Password:' "$CONN_PASSWORD"
echo 'Host: ' "$CONN_HOST"
echo 'Port: ' "$CONN_PORT"
echo 'Display details: ' "$CONN_SHOW_DETAIL"
echo 'Log level: ' "$CONN_LOG_LEVEL"
4. Description
In fact, getopt
is only responsible for rearranging parameters, and does not care about extracting parameter values. It will focus on the option parameters in the command line according to the specified parameter list, and nothing more. After this processing, it is relatively simple to parse through the code yourself. Therefore, in the above code sample, there is only one line that actually involves the use of getopt, and the rest of the code is based on the rearranged parameters of getopt, which can be further analyzed by itself.
In this example, option arguments
and non-option arguments
are not in order, so first tell the getopt
command which arguments to parse:
getopt -o 'u:p:n:vl::' -l 'username:,password:,port:,verbose,log-level::' -- "$@"
Parameter rules:
- The
-o
parameter specifies the terminal parameter format, and the-l
parameter specifies the corresponding long parameter - Colon
:
rules are basically the same asgetopts
rules. The difference is that the default value parameter is followed by two colons::
- For the
default value option
, there must be no spaces between the short parameter formal parameter name and the value, and the long parameter formal parameter name and value need to be connected with an equal sign.=