Un exemple d'utilisation de getopts dans bash
Je veux appeler myscript
fichier de cette façon:
$ ./myscript -s 45 -p any_string
Ou
$ ./myscript -h >>> should display help
$ ./myscript >>> should display help
Mes exigences sont:
-
getopt
ici pour obtenir les arguments d'entrée - vérifiez que
-s
existe, sinon renvoyez l'erreur - vérifiez que la valeur après le
-s
est 45 ou 90 - vérifier que le
-p
existe et il y a une chaîne d'entrée après - si l'utilisateur entre
./myscript -h
ou simplement./myscript
, puis afficher l'aide de
J'ai essayé jusqu'à présent code:
#!/bin/bash
while getopts "h:s:" arg; do
case $arg in
h)
echo "usage"
;;
s)
strength=$OPTARG
echo $strength
;;
esac
done
Mais avec ce code, j'obtiens des erreurs. Comment le faire avec Bash et getopt
?
6 réponses
#!/bin/bash
usage() { echo "Usage: $0 [-s <45|90>] [-p <string>]" 1>&2; exit 1; }
while getopts ":s:p:" o; do
case "${o}" in
s)
s=${OPTARG}
((s == 45 || s == 90)) || usage
;;
p)
p=${OPTARG}
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${s}" ] || [ -z "${p}" ]; then
usage
fi
echo "s = ${s}"
echo "p = ${p}"
Exemple d'exécution:
$ ./myscript.sh
Usage: ./myscript.sh [-s <45|90>] [-p <string>]
$ ./myscript.sh -h
Usage: ./myscript.sh [-s <45|90>] [-p <string>]
$ ./myscript.sh -s "" -p ""
Usage: ./myscript.sh [-s <45|90>] [-p <string>]
$ ./myscript.sh -s 10 -p foo
Usage: ./myscript.sh [-s <45|90>] [-p <string>]
$ ./myscript.sh -s 45 -p foo
s = 45
p = foo
$ ./myscript.sh -s 90 -p bar
s = 90
p = bar
Le problème avec le code d'origine est que:
-
h:
attend le paramètre où il ne devrait pas, alors changez-le en justeh
(sans deux-points) - attendre
-p any_string
, vous avez besoin d'ajouterp:
à la liste d'arguments
La syntaxe de base de getopts
(voir: man bash
):
getopts OPTSTRING VARNAME [ARGS...]
Où:
-
OPTSTRING
est une chaîne avec la liste des arguments attendus,-
h
- vérifier pour l'option-h
sans paramètres; donne erreur sur les options non prises en charge; -
h:
- Vérifier l'option-h
avec paramètre; donne des erreurs sur les options non prises en charge; -
abc
- vérifier les options-a
,-b
,-c
; donne erreurs sur les options non pris en charge; -
:abc
- vérifier les options-a
,-b
,-c
; les silences erreurs sur les options non pris en charge;Notes: En d'autres termes, deux points devant les options vous permet de gérer les erreurs dans votre code. Variable contiendra
?
dans le cas d'une option non prise en charge,:
dans le cas d'une valeur manquante.
-
-
OPTARG
- est défini sur la valeur de l'argument courant, -
OPTERR
- indique si Bash doit afficher des messages d'erreur.
Donc le code peut être:
#!/usr/bin/env bash
usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; }
[ $# -eq 0 ] && usage
while getopts ":hs:p:" arg; do
case $arg in
p) # Specify p value.
echo "p is ${OPTARG}"
;;
s) # Specify strength, either 45 or 90.
strength=${OPTARG}
[ $strength -eq 45 -o $strength -eq 90 ] \
&& echo "Strength is $strength." \
|| echo "Strength needs to be either 45 or 90, $strength found instead."
;;
h | *) # Display help.
usage
exit 0
;;
esac
done
Exemple d'utilisation:
$ ./foo.sh
./foo.sh usage:
p) # Specify p value.
s) # Specify strength, either 45 or 90.
h | *) # Display help.
$ ./foo.sh -s 123 -p any_string
Strength needs to be either 45 or 90, 123 found instead.
p is any_string
$ ./foo.sh -s 90 -p any_string
Strength is 90.
p is any_string
L'exemple emballé avec getopt
(mA distribution l'a mis dans /usr/share/getopt/getopt-parse.bash
) semble couvrir tous vos cas:
#!/bin/bash
# A small example program for using the new getopt(1) program.
# This program will only work with bash(1)
# An similar program using the tcsh(1) script language can be found
# as parse.tcsh
# Example input and output (from the bash prompt):
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
# Option a
# Option c, no argument
# Option c, argument `more'
# Option b, argument ` very long '
# Remaining arguments:
# --> `par1'
# --> `another arg'
# --> `wow!*\?'
# Note that we use `"$@"' to let each command-line parameter expand to a
# separate word. The quotes around `$@' are essential!
# We need TEMP as the `eval set --' would nuke the return value of getopt.
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
-n 'example.bash' -- "$@"`
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
while true ; do
case "$1" in
-a|--a-long) echo "Option a" ; shift ;;
-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
-c|--c-long)
# c has an optional argument. As we are in quoted mode,
# an empty parameter will be generated if its optional
# argument is not found.
case "$2" in
"") echo "Option c, no argument"; shift 2 ;;
*) echo "Option c, argument \`$2'" ; shift 2 ;;
esac ;;
--) shift ; break ;;
*) echo "Internal error!" ; exit 1 ;;
esac
done
echo "Remaining arguments:"
for arg do echo '--> '"\`$arg'" ; done
Je sais que c'est déjà répondu, mais pour le compte rendu et pour toute personne ayant les mêmes requeriments que moi, j'ai décidé de poster cette réponse connexe. Le code est inondé de commentaires pour expliquer le code.
Réponse mise à jour:
Enregistrez le fichier sous getopt.sh
:
#!/bin/bash
function get_variable_name_for_option {
local OPT_DESC=${1}
local OPTION=${2}
local VAR=$(echo ${OPT_DESC} | sed -e "s/.*\[\?-${OPTION} \([A-Z_]\+\).*/\1/g" -e "s/.*\[\?-\(${OPTION}\).*/\1FLAG/g")
if [[ "${VAR}" == "${1}" ]]; then
echo ""
else
echo ${VAR}
fi
}
function parse_options {
local OPT_DESC=${1}
local INPUT=$(get_input_for_getopts "${OPT_DESC}")
shift
while getopts ${INPUT} OPTION ${@};
do
[ ${OPTION} == "?" ] && usage
VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
[ "${VARNAME}" != "" ] && eval "${VARNAME}=${OPTARG:-true}" # && printf "\t%s\n" "* Declaring ${VARNAME}=${!VARNAME} -- OPTIONS='$OPTION'"
done
check_for_required "${OPT_DESC}"
}
function check_for_required {
local OPT_DESC=${1}
local REQUIRED=$(get_required "${OPT_DESC}" | sed -e "s/\://g")
while test -n "${REQUIRED}"; do
OPTION=${REQUIRED:0:1}
VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
[ -z "${!VARNAME}" ] && printf "ERROR: %s\n" "Option -${OPTION} must been set." && usage
REQUIRED=${REQUIRED:1}
done
}
function get_input_for_getopts {
local OPT_DESC=${1}
echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}
function get_optional {
local OPT_DESC=${1}
echo ${OPT_DESC} | sed -e "s/[^[]*\(\[[^]]*\]\)[^[]*/\1/g" -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}
function get_required {
local OPT_DESC=${1}
echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/\[[^[]*\]//g" -e "s/[][ -]//g"
}
function usage {
printf "Usage:\n\t%s\n" "${0} ${OPT_DESC}"
exit 10
}
, Alors vous pouvez l'utiliser comme ceci:
#!/bin/bash
#
# [ and ] defines optional arguments
#
# location to getopts.sh file
source ./getopt.sh
USAGE="-u USER -d DATABASE -p PASS -s SID [ -a START_DATE_TIME ]"
parse_options "${USAGE}" ${@}
echo ${USER}
echo ${START_DATE_TIME}
Ancienne réponse:
J'ai récemment eu besoin d'utiliser une approche générique. Je suis tombé sur cette solution:
#!/bin/bash
# Option Description:
# -------------------
#
# Option description is based on getopts bash builtin. The description adds a variable name feature to be used
# on future checks for required or optional values.
# The option description adds "=>VARIABLE_NAME" string. Variable name should be UPPERCASE. Valid characters
# are [A-Z_]*.
#
# A option description example:
# OPT_DESC="a:=>A_VARIABLE|b:=>B_VARIABLE|c=>C_VARIABLE"
#
# -a option will require a value (the colon means that) and should be saved in variable A_VARIABLE.
# "|" is used to separate options description.
# -b option rule applies the same as -a.
# -c option doesn't require a value (the colon absense means that) and its existence should be set in C_VARIABLE
#
# ~$ echo get_options ${OPT_DESC}
# a:b:c
# ~$
#
# Required options
REQUIRED_DESC="a:=>REQ_A_VAR_VALUE|B:=>REQ_B_VAR_VALUE|c=>REQ_C_VAR_FLAG"
# Optional options (duh)
OPTIONAL_DESC="P:=>OPT_P_VAR_VALUE|r=>OPT_R_VAR_FLAG"
function usage {
IFS="|"
printf "%s" ${0}
for i in ${REQUIRED_DESC};
do
VARNAME=$(echo $i | sed -e "s/.*=>//g")
printf " %s" "-${i:0:1} $VARNAME"
done
for i in ${OPTIONAL_DESC};
do
VARNAME=$(echo $i | sed -e "s/.*=>//g")
printf " %s" "[-${i:0:1} $VARNAME]"
done
printf "\n"
unset IFS
exit
}
# Auxiliary function that returns options characters to be passed
# into 'getopts' from a option description.
# Arguments:
# $1: The options description (SEE TOP)
#
# Example:
# OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
# OPTIONS=$(get_options ${OPT_DESC})
# echo "${OPTIONS}"
#
# Output:
# "h:f:PW"
function get_options {
echo ${1} | sed -e "s/\([a-zA-Z]\:\?\)=>[A-Z_]*|\?/\1/g"
}
# Auxiliary function that returns all variable names separated by '|'
# Arguments:
# $1: The options description (SEE TOP)
#
# Example:
# OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
# VARNAMES=$(get_values ${OPT_DESC})
# echo "${VARNAMES}"
#
# Output:
# "H_VAR|F_VAR|P_VAR|W_VAR"
function get_variables {
echo ${1} | sed -e "s/[a-zA-Z]\:\?=>\([^|]*\)/\1/g"
}
# Auxiliary function that returns the variable name based on the
# option passed by.
# Arguments:
# $1: The options description (SEE TOP)
# $2: The option which the variable name wants to be retrieved
#
# Example:
# OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
# H_VAR=$(get_variable_name ${OPT_DESC} "h")
# echo "${H_VAR}"
#
# Output:
# "H_VAR"
function get_variable_name {
VAR=$(echo ${1} | sed -e "s/.*${2}\:\?=>\([^|]*\).*/\1/g")
if [[ ${VAR} == ${1} ]]; then
echo ""
else
echo ${VAR}
fi
}
# Gets the required options from the required description
REQUIRED=$(get_options ${REQUIRED_DESC})
# Gets the optional options (duh) from the optional description
OPTIONAL=$(get_options ${OPTIONAL_DESC})
# or... $(get_options "${OPTIONAL_DESC}|${REQUIRED_DESC}")
# The colon at starts instructs getopts to remain silent
while getopts ":${REQUIRED}${OPTIONAL}" OPTION
do
[[ ${OPTION} == ":" ]] && usage
VAR=$(get_variable_name "${REQUIRED_DESC}|${OPTIONAL_DESC}" ${OPTION})
[[ -n ${VAR} ]] && eval "$VAR=${OPTARG}"
done
shift $(($OPTIND - 1))
# Checks for required options. Report an error and exits if
# required options are missing.
# Using function version ...
VARS=$(get_variables ${REQUIRED_DESC})
IFS="|"
for VARNAME in $VARS;
do
[[ -v ${VARNAME} ]] || usage
done
unset IFS
# ... or using IFS Version (no function)
OLDIFS=${IFS}
IFS="|"
for i in ${REQUIRED_DESC};
do
VARNAME=$(echo $i | sed -e "s/.*=>//g")
[[ -v ${VARNAME} ]] || usage
printf "%s %s %s\n" "-${i:0:1}" "${!VARNAME:=present}" "${VARNAME}"
done
IFS=${OLDIFS}
Je n'ai pas testé cela à peu près, donc j'aurais pu quelques bugs là.
POSIX 7 exemple
, Il est également utile de vérifier l'exemple de la norme: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
aflag=
bflag=
while getopts ab: name
do
case $name in
a) aflag=1;;
b) bflag=1
bval="$OPTARG";;
?) printf "Usage: %s: [-a] [-b value] args\n" $0
exit 2;;
esac
done
if [ ! -z "$aflag" ]; then
printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"
Et puis nous pouvons l'essayer:
$ sh a.sh
Remaining arguments are:
$ sh a.sh -a
Option -a specified
Remaining arguments are:
$ sh a.sh -b
No arg for -b option
Usage: a.sh: [-a] [-b value] args
$ sh a.sh -b myval
Option -b "myval" specified
Remaining arguments are:
$ sh a.sh -a -b myval
Option -a specified
Option -b "myval" specified
Remaining arguments are:
$ sh a.sh remain
Remaining arguments are: remain
$ sh a.sh -- -a remain
Remaining arguments are: -a remain
Testé dans Ubuntu 17.10, sh
est dash 0.5.8.
Utiliser getopt
Pourquoi getopt?
Analyser les arguments de ligne de commande élaborés pour éviter toute confusion et clarifier les options que nous analysons afin que le lecteur des commandes puisse comprendre ce qui se passe.
Qu'est-Ce que getopt?
getopt
est utilisé pour décomposer (analyser) les options dans les lignes de commande pour faciliter l'analyse par les procédures shell, et pour vérifier les options légales. Il utilise les routines GNU getopt(3)
pour ce faire.
getopt
peut avoir types d'options suivants.
- options sans valeur
- options de paire clé-valeur
Note: dans ce document, lors de l'explication de la syntaxe:
- Tout ce qui se trouve à l'intérieur de [] est un paramètre optionnel dans la syntaxe / exemples. {[35] } est un détenteur de place, ce qui signifie qu'il devrait être remplacé par une valeur réelle.
COMMENT UTILISER getopt
?
Syntaxe: Première Forme
getopt optstring parameters
Exemples:
# This is correct
getopt "hv:t::" "-v 123 -t123"
getopt "hv:t::" "-v123 -t123" # -v and 123 doesn't have whitespace
# -h takes no value.
getopt "hv:t::" "-h -v123"
# This is wrong. after -t can't have whitespace.
# Only optional params cannot have whitespace between key and value
getopt "hv:t::" "-v 123 -t 123"
# Multiple arguments that takes value.
getopt "h:v:t::g::" "-h abc -v 123 -t21"
# Multiple arguments without value
# All of these are correct
getopt "hvt" "-htv"
getopt "hvt" "-h -t -v"
getopt "hvt" "-tv -h"
Ici h, v, t sont les options et-h-v-t est la façon dont les options doivent être données en ligne de commande.
- ' h ' est une option sans valeur.
- 'v:' implique que l'option-v a une valeur et est une option obligatoire. ':' a une valeur.
- 't::' implique que l'option-t a une valeur mais est facultative. ":: ", facultatif.
Dans optional param, value ne peut pas avoir de séparation d'espaces avec l'option. Ainsi, dans l'exemple "- t123",- t est l'option 123 est la valeur.
Syntaxe: Deuxième Formulaire
getopt [getopt_options] [--] [optstring] [parameters]
Ici après getopt est divisé en cinq parties
- la commande elle-même, c'est-à-dire getopt
- Le getopt_options, il décrit comment analyser les arguments. unique dash options longues, Double Dash options.
- --, sépare les getopt_options des options que vous souhaitez analyser et des options courtes autorisées
- les options courtes, sont prises immédiatement après -- est trouvé. Tout comme la Forme première de la syntaxe.
- Les paramètres, ce sont les options que vous avez passées dans le programme. Les options que vous souhaitez analyser et obtenir les valeurs réelles définies sur eux.
Exemples
getopt -l "name:,version::,verbose" -- "n:v::V" "--name=Karthik -version=5.2 -verbose"
Syntaxe: Troisième Forme
getopt [getopt_options] [-o options] [--] [optstring] [parameters]
Ici après getopt est divisé en cinq parties
- la commande elle-même, c'est-à-dire getopt
- Le getopt_options, il décrit comment analyser les arguments. unique dash options longues, Double Dash options.
- les options courtes, c'est-à-dire-O ou --options. Tout comme la forme d'abord syntaxe mais avec l'option "- o "et avant le" -- " (double tiret).
- --, sépare les getopt_options des options que vous souhaitez analyser et des options courtes autorisées
- les paramètres, ce sont les options que vous avez passées dans le programme. Les options que vous souhaitez analyser et obtenir les valeurs réelles définies sur eux.
Exemples
getopt -l "name:,version::,verbose" -a -o "n:v::V" -- "-name=Karthik -version=5.2 -verbose"
GETOPT_OPTIONS
Getopt_options change la façon dont les paramètres de ligne de commande sont analyser.
Voici quelques-unes des getopt_options
Option: - L ou -- longoptions
Signifie que la commande getopt doit autoriser les options multi-caractères reconnu. Plusieurs options sont séparées par des virgules.
Par exemple, --name=Karthik
est une option longue envoyée en ligne de commande. Dans getopt, l'utilisation des options longues est comme
getopt "name:,version" "--name=Karthik"
Puisque name: est spécifié, l'option doit contenir une valeur
Option: -un ou -- alternative
Signifie que la commande getopt devrait permettre à long option d'avoir un seul tiret '- 'plutôt que Double dash'--'.
Exemple, au lieu de --name=Karthik
vous pouvez utiliser seulement -name=Karthik
getopt "name:,version" "-name=Karthik"
Un script complet exemple avec le code:
#!/bin/bash
# filename: commandLine.sh
# author: @theBuzzyCoder
showHelp() {
# `cat << EOF` This means that cat should stop reading when EOF is detected
cat << EOF
Usage: ./installer -v <espo-version> [-hrV]
Install Pre-requisites for EspoCRM with docker in Development mode
-h, -help, --help Display help
-v, -espo-version, --espo-version Set and Download specific version of EspoCRM
-r, -rebuild, --rebuild Rebuild php vendor directory using composer and compiled css using grunt
-V, -verbose, --verbose Run script in verbose mode. Will print out each step of execution.
EOF
# EOF is found above and hence cat command stops reading. This is equivalent to echo but much neater when printing out.
}
export version=0
export verbose=0
export rebuilt=0
# $@ is all command line parameters passed to the script.
# -o is for short options like -v
# -l is for long options with double dash like --version
# the comma separates different long options
# -a is for long options with single dash like -version
options=$(getopt -l "help,version:,verbose,rebuild,dryrun" -o "hv:Vrd" -a -- "$@")
# set --:
# If no arguments follow this option, then the positional parameters are unset. Otherwise, the positional parameters
# are set to the arguments, even if some of them begin with a ‘-’.
eval set -- "$options"
while true
do
case $1 in
-h|--help)
showHelp
exit 0
;;
-v|--version)
shift
export version=$1
;;
-V|--verbose)
export verbose=1
set -xv # Set xtrace and verbose mode.
;;
-r|--rebuild)
export rebuild=1
;;
--)
shift
break;;
esac
shift
done
Exécution de ce fichier de script:
# With short options grouped together and long option
# With double dash '--version'
bash commandLine.sh --version=1.0 -rV
# With short options grouped together and long option
# With single dash '-version'
bash commandLine.sh -version=1.0 -rV
# OR with short option that takes value, value separated by whitespace
# by key
bash commandLine.sh -v 1.0 -rV
# OR with short option that takes value, value without whitespace
# separation from key.
bash commandLine.sh -v1.0 -rV
# OR Separating individual short options
bash commandLine.sh -v1.0 -r -V