505 votes

Un exemple de l'utilisation de getopts en bash

Je veux appeler myscript de cette manière :

$ ./myscript -s 45 -p any_string

ou

$ ./myscript -h  #should display help
$ ./myscript     #should display help

Mes exigences sont les suivantes :

  • getopt ici pour obtenir les arguments d'entrée
  • vérifiez que -s existe, sinon retour d'une erreur
  • vérifiez que la valeur après le -s est de 45 ou 90
  • vérifier que le -p existe et il y a une chaîne d'entrée après
  • si l'utilisateur saisit ./myscript -h ou simplement ./myscript puis afficher l'aide

J'ai essayé jusqu'à présent ce 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 votes

Les options sont censées être facultatives. Si vous avez besoin de la valeur spécifiée par -s pour en faire un argument de position : ./myscript 45 anystring .

1 votes

@chepner $./myscript -s 45 -p any_string

2 votes

C'est bien si -p est en fait une option (c'est-à-dire que votre programme peut continuer s'il n'est pas présent). Dans ce cas, ./myscript 45 -p any_string . (Je pense que getopt peut gérer des options mixtes et des arguments positionnels, alors que la méthode bash commande intégrée getopts exige que tous les arguments positionnels soient placés après les options).

688voto

Adrian Frühwirth Points 8883
#!/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}"

Exemples 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

46 votes

Dans l'appel getopts, pourquoi y a-t-il un deux-points en tête ? Quand est-ce que "h" est suivi de deux points ?

14 votes

Devrait usage() renvoie vraiment 1 ?

8 votes

@Pithikos Bon point. Le bon sens me dit que lorsqu'il est invoqué via -h il devrait retourner 0 si un drapeau inexistant est touché, il devrait retourner >0 (pour des raisons de simplicité, je n'ai pas fait de différence entre ces deux cas et personne ne vous oblige à imprimer le texte d'utilisation dans le dernier cas). J'ai vu des programmes qui retournent toujours != 0 Cependant, même sur -h/--help . Peut-être devrais-je mettre à jour le snippet au cas où les gens l'utiliseraient comme modèle (j'espère que non) ?

200voto

kenorb Points 2464

Le problème avec le code original est que :

  • h: attend un paramètre là où il ne devrait pas, donc changez-le en simplement h (sans les deux points)
  • à attendre -p any_string vous devez ajouter p: à la liste d'arguments

En gros, : après l'option signifie qu'elle requiert l'argument.


La syntaxe de base de getopts est (voir : man bash ) :

getopts OPTSTRING VARNAME [ARGS...]

où :

  • OPTSTRING est une chaîne de caractères avec la liste des arguments attendus,

    • h - vérifier l'option -h sans paramètres ; donne une erreur sur les options non supportées ;
    • h: - vérifier l'option -h avec donne des erreurs sur les options non supportées ;
    • abc - vérifier les options -a , -b , -c ; donne les erreurs sur les options non prises en charge ;
    • :abc - vérifier les options -a , -b , -c ; fait taire les erreurs sur les options non prises en charge ;

      Notes : En d'autres termes, les deux points devant les options vous permettent de gérer les erreurs dans votre code. La variable contiendra ? dans le cas d'une option non prise en charge, : en cas de valeur manquante.

  • OPTARG - est fixé à la valeur de l'argument actuel,

  • 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

Voir : Petit tutoriel sur les getopts sur Bash Hackers Wiki

142voto

theBuzzyCoder Points 1086

Utilisez getopt

Pourquoi getopt ?

Analyser les arguments élaborés de la ligne de commande pour éviter toute confusion et clarifier les options que nous analysons afin que les lecteurs des commandes puissent comprendre ce qui se passe.

Qu'est-ce que getopt ?

getopt est utilisé pour séparer (analyser) les options dans les lignes de commande pour faciliter l'analyse par les procédures de l'interpréteur de commandes, et pour vérifier les options légales. Il utilise l'algorithme GNU getopt(3) des routines pour ce faire.

getopt peut avoir les types d'options suivants.

  1. Options sans valeur
  2. options de paires 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 facultatif dans la syntaxe/les exemples.
  • est un caractère de remplacement, ce qui signifie qu'il doit être remplacé par une valeur réelle.

MODE D'EMPLOI 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 dans la ligne de commande.

  1. h" est une option sans valeur.
  2. v:' implique que l'option -v a une valeur et que est une option obligatoire. ':' signifie a une valeur.
  3. t::' implique que l'option -t a une valeur mais est facultative. '::' signifie facultatif.

Dans un paramètre optionnel, la valeur ne peut pas être séparée par un espace avec l'option. Ainsi, dans l'exemple "-t123", -t est l'option 123 est la valeur.

Syntaxe : Deuxième forme

getopt [getopt_options] [--] optstring parameters

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. simple tiret options longues, double tiret options.
  • --, sépare les options getopt_options des options que vous voulez analyser et des options courtes autorisées.
  • Les options courtes, sont prises immédiatement après que -- soit trouvé. Tout comme la syntaxe Form first.
  • Les paramètres, ce sont les options que vous avez passées au programme. Les options que vous voulez analyser et obtenir les valeurs réelles qui leur sont attribuées.

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 [getopt_options] [--] [parameters]

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. simple tiret options longues, double tiret options.
  • Les options courtes, c'est-à-dire -o ou --options. Comme la première syntaxe de la forme, mais avec l'option "-o" et avant le "--" (double tiret).
  • --, sépare les options getopt_options des options que vous voulez analyser et des options courtes autorisées.
  • Les paramètres, ce sont les options que vous avez passées au programme. Les options que vous voulez analyser et obtenir les valeurs réelles qui leur sont attribuées.

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 la ligne de commande sont analysés.

Voici quelques-unes des options de getopt_options

Option : -l ou --longoptions

Les moyens de la commande getopt devraient permettre aux options à plusieurs caractères d'être reconnues. Les options multiples sont séparées par une virgule.

Par exemple, --name=Karthik est une option longue envoyée dans la ligne de commande. Dans getopt, les options longues sont utilisées comme suit

getopt -l "name:,version" -- "" --name=Karthik

Puisque name : est spécifié, l'option doit contenir une valeur

Option : -a ou --alternative

La commande getopt devrait permettre aux options longues d'avoir un simple tiret plutôt qu'un double tiret '--'.

Exemple, au lieu de --name=Karthik vous pourriez utiliser seulement -name=Karthik

getopt -a -l "name:,version" -- "" -name=Karthik

Un exemple complet de script 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 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

44voto

Brian Cain Points 7150

L'exemple fourni avec getopt (ma distribution le met 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

12 votes

La commande externe getopt(1) n'est jamais sûre à utiliser, sauf si vous connaître il s'agit de GNU getopt, vous l'appelez d'une manière spécifique à GNU, y vous assurez que GETOPT_COMPATIBLE n'est pas dans l'environnement. Utilisez getopts (intégré au shell) à la place, ou bouclez simplement sur les paramètres positionnels.

0 votes

@sputnick, tyvm, je ne le savais pas.

17 votes

Eh, aucune commande externe n'est sûre à utiliser selon cette norme. Le getopts intégré manque de fonctionnalités cruciales et si vous voulez vérifier GETOPT_COMPATIBLE, c'est plus facile que de porter les fonctionnalités de getopt.

13voto

Sebastian Points 845

Je sais qu'il a déjà été répondu à cette question, mais pour mémoire et pour toute personne ayant les mêmes exigences que moi, j'ai décidé de poster cette réponse connexe. Le code est inondé de commentaires pour expliquer le code.

Réponse actualisée :

Enregistrez le fichier comme 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}

Vieille 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 de manière approximative, donc il pourrait y avoir quelques bugs.

1 votes

Si vous utilisez getopts dans une fonction, ajoutez local OPTIND OPTARG à la fonction

0 votes

@glennjackman en fait c'est plutôt une approche sed que d'utiliser getopts

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X