Un exemple d'utilisation de getopts dans bash

345

Je veux appeler le myscriptfichier 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érifier qu'il -sexiste, sinon renvoyer une erreur
  • vérifier que la valeur après le -sest 45 ou 90
  • vérifier que le -pexiste et qu'il y a une chaîne d'entrée après
  • si l'utilisateur entre ./myscript -hou ./myscriptaffiche alors 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 faire avec Bash et getopt?

MOHAMED
la source
2
Les options sont censées être facultatives. Si vous avez besoin de la valeur spécifiée par -s, faire un argument de position: ./myscript 45 anystring.
chepner
@chepner$./myscript -s 45 -p any_string
MOHAMED
C'est bien si -pc'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 cela getoptpeut gérer des options mixtes et des arguments positionnels, alors que la bashcommande intégrée getoptsnécessite que tous les arguments positionnels soient placés après les options.)
chepner

Réponses:

513
#!/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}"

L'exemple s'exécute:

$ ./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
Adrian Frühwirth
la source
19
Dans l'appel getopts, pourquoi y a-t-il un côlon principal? Quand "h" a-t-il un colon après?
e40
7
Doit usage()vraiment renvoyer 1?
Pithikos
6
@Pithikos Bon point. Le bon sens me dit que lorsqu'il est invoqué via, -hil devrait revenir 0, en frappant un indicateur inexistant, il devrait revenir >0(pour des raisons de simplicité, je n'ai pas fait de différence entre ces cas et personne ne vous oblige à imprimer le texte d'utilisation dans ce dernier cas) . J'ai vu des programmes qui reviennent toujours != 0, même sur -h/--help. Peut-être que je devrais mettre à jour l'extrait au cas où les gens l'utilisent comme passe-partout (j'espère que non)?
Adrian Frühwirth
1
@ A.Danischewski C'est par ( getopts') conception, il n'y a pas de "arguments optionnels" avec getopts. L'analyseur ne peut tout simplement pas savoir si le prochain jeton est un argument de l'option actuelle ou une option en soi car il -ppourrait s'agir de la valeur souhaitée. Vous pouvez contourner cela si vous savez absolument qu'un paramètre d'option ne peut pas ressembler à une autre option valide, oui, mais on pourrait dire qu'il y a une raison pour laquelle les arguments facultatifs ne sont pas définis dans POSIX.
Adrian Frühwirth
4
@ user1011471 Vous avez raison! Les accolades, pour ainsi dire, aident simplement le bashlexer à identifier les variables. Dans de nombreux cas, ils sont inutiles et le fait que je les utilise toujours n'est qu'une question de style de codage personnel. Pour moi, il est plus facile (et plus joli) de toujours les utiliser au lieu de se souvenir des règles d'analyse en ce qui concerne l'ambiguïté. À peu près la même raison pour laquelle on écrirait if (foo) { bar; }au lieu de if (foo) bar;dans un langage de style C (esthétique et / ou éviter les erreurs stupides).
Adrian Frühwirth
109

Le problème avec le code d'origine est que:

  • h:attend un paramètre où il ne devrait pas, alors changez-le en juste h(sans deux-points)
  • à prévoir -p any_string, vous devez ajouter p:à la liste des arguments

Fondamentalement, :après que l'option signifie qu'elle nécessite l'argument.


La syntaxe de base de getoptsest (voir:) man bash:

getopts OPTSTRING VARNAME [ARGS...]

où:

  • OPTSTRING est une chaîne avec la liste des arguments attendus,

    • h- vérifier l'option -h sans paramètres; donne une erreur sur les options non prises en charge;
    • h:- vérifier l'option -h avec le paramètre; donne des erreurs sur les options non prises en charge;
    • abc- vérification des options -a, -b, -c; donne des erreurs sur les options non prises en charge;
    • :abc- vérification des options -a, -b, -c; réduit au silence les erreurs sur les options non prises en charge;

      Remarques: En d'autres termes, 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, :dans le cas d'une valeur manquante.

  • OPTARG - est défini sur la valeur d'argument actuelle,

  • OPTERR - indique si Bash doit afficher des messages d'erreur.

Le code peut donc ê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 getopts sur Bash Hackers Wiki

kenorb
la source
2
Changer la fonction d'utilisation à ceci: usage() { echo "$0 usage:" && grep "[[:space:]].)\ #" $0 | sed 's/#//' | sed -r 's/([a-z])\)/-\1/'; exit 0; }. Il ne prend en compte qu'un seul espace blanc avant l'option lettre, supprime le # du commentaire et ajoute un «-» avant l'option lettre pour le rendre plus clair pour la commande.
poagester
2
@kenorb: les deux points devant les options n'ignorent pas les options non prises en charge mais réduisent au silence les erreurs de bash et vous permettent de les gérer dans votre code. La variable contiendra '?' dans le cas d'une option non prise en charge et «:» dans le cas d'une valeur manquante.
Hynek -Pichi- Vychodil
1
Merci pour les documents détaillés, :je n'ai pas pu obtenir le bon avant d'avoir vu ces notes. Nous devons ajouter un :aux options où nous attendons un argument.
Aukhan
51

Utilisation 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?

getoptest utilisé pour décomposer (analyser) les options dans les lignes de commande pour une analyse facile par les procédures shell et pour vérifier les options légales. Il utilise les getopt(3)routines GNU pour ce faire.

getopt peut avoir les types d'options suivants.

  1. Options sans valeur
  2. options de paire clé-valeur

Remarque: 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 / exemples.
  • est un espace réservé, ce qui signifie qu'il doit être remplacé par une valeur réelle.

COMMENT UTILISER getopt?

Syntaxe: premier formulaire

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.

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

Dans param facultatif, la valeur 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 ie getopt
  • Le getopt_options, il décrit comment analyser les arguments. options longues simple tiret, options double tiret.
  • -, sépare les options 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ée. Tout comme la première syntaxe du formulaire.
  • 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 qui y sont définies.

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 ie getopt
  • Le getopt_options, il décrit comment analyser les arguments. options longues simple tiret, options double tiret.
  • Les options courtes, à savoir -o ou --options. Tout comme la première syntaxe du formulaire mais avec l'option "-o" et avant le "-" (double tiret).
  • -, sépare les options 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 qui y sont définies.

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 analysés.

Voici quelques-unes des options getopt_options

Option: -l ou --longoptions

Signifie que la commande getopt doit permettre de reconnaître les options à plusieurs caractères. Plusieurs options sont séparées par une virgule.

Par exemple, --name=Karthikune longue option est envoyée en ligne de commande. Dans getopt, l'utilisation des options longues est comme

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

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

Option: -a ou --alternative

Signifie que la commande getopt doit permettre à l'option longue d'avoir un seul tiret '-' plutôt qu'un double tiret '-'.

Par exemple, au lieu de --name=Karthikvous pouvez utiliser simplement-name=Karthik

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

Un exemple de script complet 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
theBuzzyCoder
la source
source: linkedin.com/pulse/…
theBuzzyCoder
getopt vs getopts .. une conformité multiplateforme très différente
shadowbq
35

L'exemple fourni avec getopt(ma distribution l'a mis /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
Brian Cain
la source
11
La commande externe getopt (1) n'est jamais sûre à utiliser, sauf si vous savez qu'il s'agit de GNU getopt, que vous l'appelez d'une manière spécifique à GNU et que vous vous assurez que GETOPT_COMPATIBLE n'est pas dans l'environnement. Utilisez plutôt getopts (shell intégré), ou bouclez simplement sur les paramètres de position.
Gilles Quenot
@sputnick, tyvm, ne savait pas cela.
Brian Cain
14
Eh, aucune commande externe n'est sûre à utiliser selon cette norme. 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.
Michael Terry
12

Je sais que cela a déjà été répondu, mais pour le compte rendu 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 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
}

Ensuite, 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 grossièrement, donc je pourrais avoir des bugs là-dedans.

Sébastien
la source
1
Si vous utilisez getoptsune fonction, ajoutez local OPTIND OPTARGà la fonction
glenn jackman
@glennjackman en fait, cela ressemble plus à une approche sed qu'à une utilisationgetopts
Sebastian
8

Exemple POSIX 7

Il convient également 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, shest le tiret 0.5.8.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
0

"getops" et "getopt" sont très limités. Bien que "getopt" soit suggéré de ne pas être utilisé du tout, il offre de longues options. Où "getopts" n'autorise que les options de caractère unique telles que "-a" "-b". Il y a quelques inconvénients supplémentaires lors de l'utilisation de l'un ou l'autre.

J'ai donc écrit un petit script qui remplace "getopts" et "getopt". C'est un début, il pourrait probablement être beaucoup amélioré.

Mise à jour 08-04-2020 : j'ai ajouté la prise en charge des traits d'union, par exemple "--package-name".

Utilisation: "./script.sh package install --package" nom avec espace "--build --archive"

# Example:
# parseArguments "${@}"
# echo "${ARG_0}" -> package
# echo "${ARG_1}" -> install
# echo "${ARG_PACKAGE}" -> "name with space"
# echo "${ARG_BUILD}" -> 1 (true)
# echo "${ARG_ARCHIVE}" -> 1 (true)
function parseArguments() {
  PREVIOUS_ITEM=''
  COUNT=0
  for CURRENT_ITEM in "${@}"
  do
    if [[ ${CURRENT_ITEM} == "--"* ]]; then
      printf -v "ARG_$(formatArgument "${CURRENT_ITEM}")" "%s" "1" # could set this to empty string and check with [ -z "${ARG_ITEM-x}" ] if it's set, but empty.
    else
      if [[ $PREVIOUS_ITEM == "--"* ]]; then
        printf -v "ARG_$(formatArgument "${PREVIOUS_ITEM}")" "%s" "${CURRENT_ITEM}"
      else
        printf -v "ARG_${COUNT}" "%s" "${CURRENT_ITEM}"
      fi
    fi

    PREVIOUS_ITEM="${CURRENT_ITEM}"
    (( COUNT++ ))
  done
}

# Format argument.
function formatArgument() {
  ARGUMENT="${1^^}" # Capitalize.
  ARGUMENT="${ARGUMENT/--/}" # Remove "--".
  ARGUMENT="${ARGUMENT//-/_}" # Replace "-" with "_".
  echo "${ARGUMENT}"
}
Twoez
la source