Question

Je souhaite que des formes longues et courtes d'options de ligne de commande soient appelées à l'aide de mon script shell.

Je sais que getopts peut être utilisé, mais comme en Perl, je n'ai pas été capable de faire la même chose avec shell.

Toutes les idées sur la façon dont cela peut être fait, afin que je puisse utiliser des options telles que:

./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/

Dans ce qui précède, les deux commandes signifient la même chose pour mon shell, mais avec getopts , je n'ai pas été en mesure de les implémenter?

Était-ce utile?

La solution

Trois applications peuvent être envisagées:

  • Bash intégré getopts . Cela ne prend pas en charge les noms d'option longs avec le préfixe de double tiret. Il ne prend en charge que les options à caractère unique.

  • Implémentation BSD UNIX de la commande autonome getopt (utilisée par MacOS). Cela ne prend pas non plus en charge les options longues.

  • Implémentation GNU de getopt autonome. GNU getopt (3) (utilisé par la ligne de commande getopt (1) sous Linux) prend en charge l'analyse d'options longues.

Certaines autres réponses montrent une solution permettant d’utiliser le getopts intégré à bash pour imiter de longues options. Cette solution constitue en fait une option courte dont le caractère est "-". Vous obtenez donc " - " comme le drapeau. Ensuite, tout ce qui suit devient OPTARG et vous testez OPTARG avec un casse imbriqué .

C’est intelligent, mais il comporte des mises en garde:

  • getopts ne peut pas appliquer la spécification opt. Il ne peut pas renvoyer d'erreurs si l'utilisateur fournit une option non valide. Vous devez effectuer votre propre vérification des erreurs lors de l'analyse de OPTARG.
  • OPTARG est utilisé pour le nom de l'option longue, ce qui complique l'utilisation lorsque votre option longue a elle-même un argument. Vous finissez par avoir à coder cela vous-même comme cas supplémentaire.

Ainsi, s’il est possible d’écrire plus de code pour pallier le manque de prise en charge des options longues, c’est beaucoup plus de travail et annule en partie le but de l’utilisation d’un analyseur getopt pour simplifier votre code.

Autres conseils

getopt et getopts sont des bêtes différentes, et les gens semblent avoir un peu de mal à comprendre ce qu'ils font. getopts est une commande intégrée permettant à bash de traiter les options de ligne de commande dans une boucle et d'affecter tour à tour les options trouvées et les valeurs aux variables intégrées. ensuite les traiter. getopt , cependant, est un utilitaire externe, et il ne traite pas vos options pour vous comme vous le feriez par exemple. bash getopts , le module Perl Getopt ou les modules Python optparse / argparse . Tout ce que getopt fait, c'est canoniser les options qui sont passées dans & # 8212; c'est-à-dire les convertir en un formulaire plus standard, de sorte qu'un script shell puisse les traiter plus facilement. Par exemple, une application de getopt peut convertir les éléments suivants:

myscript -ab infile.txt -ooutfile.txt

dans ceci:

myscript -a -b -o outfile.txt infile.txt

Vous devez effectuer vous-même le traitement. Vous n'avez pas du tout besoin d'utiliser getopt si vous définissez diverses restrictions sur la manière de spécifier des options:

  • ne met qu'une option par argument;
  • toutes les options vont avant tout paramètre de position (c'est-à-dire des arguments sans option);
  • pour les options avec des valeurs (par exemple, -o ci-dessus), la valeur doit être un argument séparé (après un espace).

Pourquoi utiliser getopt au lieu de getopts ? La raison fondamentale est que seul getopt de GNU vous permet de prendre en charge les options de ligne de commande portant un nom long. 1 (GNU getopt est la valeur par défaut sous Linux. Mac OS X et FreeBSD sont fournis avec un getopt de base peu utile, mais la version GNU peut être installée (voir ci-dessous).

Par exemple, voici un exemple d'utilisation de GNU getopt , à partir d'un de mes scripts appelé javawrap :

# NOTE: This requires GNU getopt.  On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \
             -n 'javawrap' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
  case "$1" in
    -v | --verbose ) VERBOSE=true; shift ;;
    -d | --debug ) DEBUG=true; shift ;;
    -m | --memory ) MEMORY="$2"; shift 2 ;;
    --debugfile ) DEBUGFILE="$2"; shift 2 ;;
    --minheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
    --maxheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Ceci vous permet de spécifier des options telles que - verbose -dm4096 --minh = 20 --maxhe 40 --debugfi = "/ Utilisateurs / John Johnson / debug.txt" ou similaire. L’appel de getopt a pour effet de canoniser les options de - verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile " / Utilisateurs / John Johnson / debug.txt " pour pouvoir les traiter plus facilement. La citation autour de "$ 1" et de "2 $" est importante car elle permet de gérer correctement les arguments comportant des espaces.

Si vous supprimez les 9 premières lignes (tout le long de la ligne eval set ), le code fonctionnera toujours ! Toutefois, votre code sera beaucoup plus sélectif quant aux types d’options qu’il accepte: En particulier, vous devrez spécifier toutes les options dans la section "canonique". forme décrite ci-dessus. Avec l’utilisation de getopt , vous pouvez toutefois regrouper les options à lettre unique, utiliser des formes courtes non ambiguës d’options longues, soit utiliser le - fichier foo.txt . ou style - file = foo.txt , utilisez le style -m 4096 ou -m4096 , les options de mixage et les non-options getopt génère également un message d'erreur si des options non reconnues ou ambiguës sont trouvées.

NOTE : Il existe en fait deux versions totalement différentes de getopt , de base getopt et de GNU . getopt , avec des fonctionnalités différentes et des conventions d'appel différentes. 2 Basic getopt est tout à fait en panne: non seulement il ne gère pas les options longues, il ne peut même pas gère les espaces incorporés à l'intérieur d'arguments ou d'arguments vides, alors que getopts le fait correctement. Le code ci-dessus ne fonctionnera pas dans le getopt de base. GNU getopt est installé par défaut sous Linux, mais sous Mac OS X et FreeBSD, il doit être installé séparément. Sur Mac OS X, installez MacPorts ( http://www.macports.org ), puis effectuez l'installation du port sudo getopt pour installer GNU getopt (généralement dans / opt / local / bin ) et assurez-vous que / opt / local / bin est présent votre chemin d'accès au shell devant / usr / bin . Sur FreeBSD, installez misc / getopt .

Petit guide pour la modification du code d'exemple de votre propre programme: sur les premières lignes, tout est "standard". cela devrait rester le même, sauf la ligne qui appelle getopt . Vous devez modifier le nom du programme après -n , spécifier des options courtes après -o et des options longues après - long . Placez deux points après les options qui prennent une valeur.

Enfin, si vous voyez du code qui vient d'être set au lieu de eval set , il a été écrit pour BSD getopt . Vous devriez le changer pour utiliser le style eval set , qui fonctionne bien avec les deux versions de getopt , tandis que le set brut ne fonctionne pas correctement avec GNU getopt .

1 En fait, getopts dans ksh93 prend en charge les options portant un nom long, mais ce shell n'est pas utilisé aussi souvent que bash . Dans zsh , utilisez zparseopts pour obtenir cette fonctionnalité.

2 Techniquement, "GNU getopt ". est un abus de langage; cette version a été écrite pour Linux plutôt que pour le projet GNU. Cependant, il respecte toutes les conventions GNU et le terme "GNU getopt ". est couramment utilisé (par exemple sur FreeBSD).

La fonction getopts intégrée à Bash peut être utilisée pour analyser de longues options en insérant un tiret suivi de deux points dans optspec:

#!/usr/bin/env bash 
optspec=":hv-:"
while getopts "$optspec" optchar; do
    case "${optchar}" in
        -)
            case "${OPTARG}" in
                loglevel)
                    val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                    echo "Parsing option: '--${OPTARG}', value: '${val}'" >&2;
                    ;;
                loglevel=*)
                    val=${OPTARG#*=}
                    opt=${OPTARG%=$val}
                    echo "Parsing option: '--${opt}', value: '${val}'" >&2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h)
            echo "usage: 
$ ./getopts_test.sh
$ ./getopts_test.sh -f
Non-option argument: '-f'
$ ./getopts_test.sh -h
usage: code/getopts_test.sh [-v] [--loglevel[=]<value>]
$ ./getopts_test.sh --help
$ ./getopts_test.sh -v
Parsing option: '-v'
$ ./getopts_test.sh --very-bad
$ ./getopts_test.sh --loglevel
Parsing option: '--loglevel', value: ''
$ ./getopts_test.sh --loglevel 11
Parsing option: '--loglevel', value: '11'
$ ./getopts_test.sh --loglevel=11
Parsing option: '--loglevel', value: '11'
[-v] [--loglevel[=]<value>]" >&2 exit 2 ;; v) echo "Parsing option: '-${optchar}'" >&2 ;; *) if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then echo "Non-option argument: '-${OPTARG}'" >&2 fi ;; esac done

Après avoir copié le fichier exécutable, nom = getopts_test.sh dans le répertoire de travail en cours. , on peut produire une sortie comme

getopts -- "-:"  ## without the option terminator "-- " bash complains about "-:"
getopts "-:"     ## this works in the Debian Almquist shell ("dash")

De toute évidence, getopts n’effectue pas de vérification OPTERR ni d’analyse d’argument option pour les options longues. Le fragment de script ci-dessus montre comment cela peut être fait manuellement. Le principe de base fonctionne également dans le shell Debian Almquist ("dash"). Notez le cas particulier:

<*>

Notez que, comme GreyCat, à partir de http://mywiki.wooledge.org/BashFAQ points Cette astuce exploite un comportement non standard du shell qui permet de concaténer l'option (c'est-à-dire le nom de fichier dans "-f filename") avec l'option (comme dans "-fileename"). La norme POSIX indique qu'il doit y avoir un espace entre eux, ce qui, dans la cas de " - longoption " mettrait fin à l'analyse des options et transformerait toutes les options longues en arguments sans option.

La commande getopts intégrée est toujours, autant que je sache, limitée aux options à un seul caractère.

Il existe (ou existait) un programme externe getopt qui réorganiserait un ensemble d'options de sorte qu'il était plus facile à analyser. Vous pouvez également adapter cette conception aux options longues. Exemple d'utilisation:

aflag=no
bflag=no
flist=""
set -- $(getopt abf: "$@")
while [ $# -gt 0 ]
do
    case "$1" in
    (-a) aflag=yes;;
    (-b) bflag=yes;;
    (-f) flist="$flist $2"; shift;;
    (--) shift; break;;
    (-*) echo "<*>: error - unrecognized option $1" 1>&2; exit 1;;
    (*)  break;;
    esac
    shift
done

# Process remaining non-option arguments
...

Vous pouvez utiliser un schéma similaire avec une commande getoptlong .

Notez que la faiblesse fondamentale du programme externe getopt est la difficulté de gérer les arguments contenant des espaces et de préserver ces espaces avec précision. C'est pourquoi le getopts intégré est supérieur, bien que limité par le fait qu'il ne gère que les options à lettre unique.

Voici un exemple qui utilise réellement getopt avec de longues options:

aflag=no
bflag=no
cargument=none

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc: -l along,blong,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag="yes" ;;
    -b|--blong) bflag="yes" ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    (--) shift; break;;
    (-*) echo "<*>: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done

Les options longues peuvent être analysées par la structure getopts standard en tant qu '"arguments" de - "option"

.

Ceci est un shell POSIX portable et natif - aucun programme externe ni basisme n’est nécessaire.

Ce guide implémente les options longues comme arguments de l'option - , de sorte que - alpha soit vu par getopts comme - avec l'argument alpha et - bravo = foo est considéré comme - avec l'argument bravo = foo . Le vrai argument peut être récolté avec un simple remplacement: $ {OPTARG # * =} .

Dans cet exemple, -b (et sa forme longue, - bravo ) comporte une option obligatoire (notez la reconstruction manuelle de son application pour la forme longue). . Les options non booléennes aux arguments longs viennent après les signes d'égalité, par exemple. - bravo = toto (les délimiteurs d'espace pour les options longues seraient difficiles à implémenter).

Comme cela utilise getopts , cette solution prend en charge une utilisation telle que cmd -ac --bravo = foo -d FILE (qui combine les options -a et - c et entrelace les options longues avec les options standard), tandis que la plupart des autres réponses proposées ne répondent pas ou ne répondent pas.

while getopts ab:c-: arg; do
  case $arg in
    a )  ARG_A=true ;;
    b )  ARG_B="$OPTARG" ;;
    c )  ARG_C=true ;;
    - )  LONG_OPTARG="${OPTARG#*=}"
         case $OPTARG in
           alpha    )  ARG_A=true ;;
           bravo=?* )  ARG_B="$LONG_OPTARG" ;;
           bravo*   )  echo "No arg for --$OPTARG option" >&2; exit 2 ;;
           charlie  )  ARG_C=true ;;
           alpha* | charlie* )
                       echo "No arg allowed for --$OPTARG option" >&2; exit 2 ;;
           '' )        break ;; # "--" terminates argument processing
           * )         echo "Illegal option --$OPTARG" >&2; exit 2 ;;
         esac ;;
    \? ) exit 2 ;;  # getopts already reported the illegal option
  esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list

Lorsque l'argument est un tiret ( - ), il comporte deux composants supplémentaires: le nom de l'indicateur et (éventuellement) son argument. Je les délimite comme n'importe quelle commande avec le premier signe égal ( = ). $ LONG_OPTARG est donc simplement le contenu de $ OPTARG sans le nom de l'indicateur ni le signe égal.

Le casse interne implémente manuellement les options longues, il a donc besoin d'un peu de maintenance:

  • bravo =? correspond à - bravo = foo mais pas à - bravo = (remarque: casse s’arrête après le premier match)
  • bravo * suit, en notant l’argument requis manquant dans - bravo et - bravo =
  • alpha * | charlie * intercepte les arguments donnés aux options qui ne les supportent pas
  • '' est présent pour prendre en charge les non-options qui commencent par des tirets
  • * intercepte toutes les autres options longues et recrée l'erreur générée par getopts pour une option non valide

Vous n’avez pas nécessairement besoin de tous ces articles d’entretien. Par exemple, vous souhaitez peut-être que - bravo ait un argument facultatif (que -b ne peut pas prendre en charge en raison d'une limitation de getopts ). Supprimez simplement =? et le cas d'échec associé, puis appelez $ {ARG_B: = $ DEFAULT_ARG_B} la première fois que vous utilisez $ ARG_B .

Pour accepter les options longues avec des arguments délimités par des espaces, vous avez besoin d'un eval (décemment sûr):

           bravo=?* )  ARG_B="$LONG_OPTARG" ;;
           bravo )     eval "ARG_B=\"\$OPTIND\""
                       if [ -z "$ARG_B" ]; then
                         echo "No arg for --$OPTARG option" >&2; exit 2
                       fi
                       OPTIND=$((OPTIND+1)) ;;
           bravo*   )  echo "No arg for --$OPTARG option" >&2; exit 2 ;;

Cette clause supplémentaire, ajoutée directement après la version d'affectation = , exécute un eval pour interpréter la valeur du paramètre suivant l'option en cours d'interprétation. Bash peut le faire plus proprement en utilisant développement indirect , ARG_B = " $ {! OPTIND} " et Zsh peuvent le faire avec l’indicateur extension de paramètre P , < code> ARGB = " $ {(P) OPTIND} " , mais eval est requis pour être entièrement portable POSIX. $ OPTIND est un nombre faisant référence au prochain argument du shell, disons $ 2 (ce qui signifie que $ 1 est - bravo ). . Le eval interpréterait alors ARG_B = "$ 2" et, comme il est placé entre guillemets, il n’ya pas d’abus (je ne pouvais pas trouver un moyen de tromper faire quelque chose d’inapproprié).

Garantir une valeur non vide est non trivial et nécessite une vérification effective. Nous avons donc une condition et générons une erreur fatale dans ce cas. Si vous permettez qu'il soit vide, vous devrez conditionner l'incrémentation de $ OPTIND ou sinon exécuter une boucle infinie. [$ # -gt $ OPTIND] & amp; & amp; OPTIND = $ ((OPTIND + 1)) devrait faire l'affaire.

La dernière partie de cette clause d'addition incrémente $ OPTIND pour absorber correctement l'argument de l'option et passer à l'option suivante.

Jetez un coup d'oeil à shFlags , une bibliothèque de shell portable (qui signifie : sh, bash, dash, ksh, zsh sous Linux, Solaris, etc.).

L'ajout de nouveaux indicateurs est aussi simple que l'ajout d'une ligne à votre script et fournit une fonction d'utilisation générée automatiquement.

Voici un simple Bonjour, tout le monde! avec shFlag :

#!/bin/sh

# source shflags from current directory
. ./shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# say hello
echo "Hello, ${FLAGS_name}!"

Pour les systèmes d’exploitation ayant le getopt amélioré qui prend en charge les options longues (par exemple, Linux), vous pouvez effectuer les opérations suivantes:

$ ./hello_world.sh --name Kate
Hello, Kate!

Pour le reste, vous devez utiliser l'option courte:

$ ./hello_world.sh -n Kate
Hello, Kate!

Ajouter un nouvel indicateur est aussi simple que d’ajouter un nouveau appel DEFINE_ .

Utilisation de getopts avec des options et arguments courts / longs

Fonctionne avec toutes les combinaisons, par exemple:

  • foobar -f --bar
  • foobar --foo -b
  • foobar -bf --bar --foobar
  • foobar -fbFBAshorty --bar -FB --arguments = longhorn
  • foobar -fA " text shorty " -B --arguments = "texte longhorn"
  • bash foobar -F --barfoo
  • sh foobar -B --foobar - ...
  • bash ./foobar -F --bar

Quelques déclarations pour cet exemple

Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

A quoi ressemblerait la fonction Usage

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
  $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                  -f   --foo            Set foo to yes    ($bart)
                  -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

getops avec indicateurs long / short et arguments longs

while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
             --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

Sortie

##################################################################
echo "----------------------------------------------------------"
echo "RESULT short-foo      : $sfoo                                    long-foo      : $lfoo"
echo "RESULT short-bar      : $sbar                                    long-bar      : $lbar"
echo "RESULT short-foobar   : $sfoobar                                 long-foobar   : $lfoobar"
echo "RESULT short-barfoo   : $sbarfoo                                 long-barfoo   : $lbarfoo"
echo "RESULT short-arguments: $sarguments  with Argument = \"$sARG\"   long-arguments: $larguments and $lARG"

Combinaison de ce qui précède dans un script cohérent

#!/bin/bash
# foobar: getopts with short and long options AND arguments

function _cleanup ()
{
  unset -f _usage _cleanup ; return 0
}

## Clear out nested functions on exit
trap _cleanup INT EXIT RETURN

###### some declarations for this example ######
Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
   $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                    -f   --foo            Set foo to yes    ($bart)
                      -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

##################################################################    
#######  "getopts" with: short options  AND  long options  #######
#######            AND  short/long arguments               #######
while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
               --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

Une autre façon ...

# translate long options to short
for arg
do
    delim=""
    case "$arg" in
       --help) args="${args}-h ";;
       --verbose) args="${args}-v ";;
       --config) args="${args}-c ";;
       # pass through anything else
       *) [[ "${arg:0:1}" == "-" ]] || delim="\""
           args="${args}${delim}${arg}${delim} ";;
    esac
done
# reset the translated args
eval set -- $args
# now we can process with getopt
while getopts ":hvc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        c)  source $OPTARG ;;
        \?) usage ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage
        ;;
    esac
done

J'ai un peu résolu le problème de cette façon:

# A string with command options
options=$@

# An array with all the arguments
arguments=($options)

# Loop index
index=0

for argument in $options
  do
    # Incrementing index
    index=`expr $index + 1`

    # The conditions
    case $argument in
      -a) echo "key $argument value ${arguments[index]}" ;;
      -abc) echo "key $argument value ${arguments[index]}" ;;
    esac
  done

exit;

Suis-je stupide ou quelque chose? getopt et getopts sont tellement déroutants.

Si vous ne souhaitez pas utiliser la dépendance getopt , procédez comme suit:

while test $# -gt 0
do
  case $1 in

  # Normal option processing
    -h | --help)
      # usage and help
      ;;
    -v | --version)
      # version info
      ;;
  # ...

  # Special cases
    --)
      break
      ;;
    --*)
      # error unknown (long) option $1
      ;;
    -?)
      # error unknown (short) option $1
      ;;

  # FUN STUFF HERE:
  # Split apart combined short options
    -*)
      split=$1
      shift
      set -- $(echo "$split" | cut -c 2- | sed 's/./-& /g') "$@"
      continue
      ;;

  # Done with options
    *)
      break
      ;;
  esac

  # for testing purposes:
  echo "$1"

  shift
done

Bien sûr, vous ne pouvez pas utiliser les options de style long avec un tiret. Et si vous souhaitez ajouter des versions abrégées (par exemple --verbos au lieu de --verbose), vous devez les ajouter manuellement.

Toutefois, si vous souhaitez utiliser la fonctionnalité getopts avec de longues options, cette méthode est simple.

J'ai également placé cet extrait dans un résumé .

Le getopts intégré ne peut pas faire cela. Il existe un programme externe getopt (1) qui peut le faire, mais vous ne l'obtenez que sous Linux à partir du paquet util-linux . Il est livré avec un exemple de script getopt-parse.bash .

Il existe également un getopts_long écrit en tant que fonction shell.

#!/bin/bash
while getopts "abc:d:" flag
do
  case $flag in
    a) echo "[getopts:$OPTIND]==> -$flag";;
    b) echo "[getopts:$OPTIND]==> -$flag";;
    c) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
    d) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
  esac
done

shift $((OPTIND-1))
echo "[otheropts]==> $@"

exit

.

#!/bin/bash
until [ -z "$1" ]; do
  case $1 in
    "--dlong")
      shift
      if [ "${1:1:0}" != "-" ]
      then
        echo "==> dlong $1"
        shift
      fi;;
    *) echo "==> other $1"; shift;;
  esac
done
exit

Dans ksh93 , getopts prend en charge les noms longs ...

while getopts "f(file):s(server):" flag
do
    echo "$flag" $OPTIND $OPTARG
done

Ou alors les tutoriels que j'ai trouvés ont dit. Essayez et voyez.

Inventer une autre version de la roue ...

Cette fonction est un remplacement de shell simple bourne compatible POSIX pour GNU getopt. Il supporte les options courtes / longues pouvant accepter des arguments obligatoires / optionnels / non, et la façon dont les options sont spécifiées est presque identique à GNU getopt, la conversion est donc triviale.

Bien sûr, cela reste un gros morceau de code à insérer dans un script, mais il représente environ la moitié des lignes de la fonction shell getopt_long bien connue et peut être préférable dans les cas où vous souhaitez simplement remplacer les utilisations GNU getopt existantes. .

C'est un nouveau code, donc YMMV (et s'il vous plait, dites-moi s'il ne s'agit pas d'une compatibilité POSIX pour quelque raison que ce soit - la portabilité était l'intention initiale, mais je n'ai pas de test POSIX utile environnement).

Le code et les exemples d'utilisation suivent:

#!/bin/sh
# posix_getopt shell function
# Author: Phil S.
# Version: 1.0
# Created: 2016-07-05
# URL: http://stackoverflow.com/a/37087374/324105

# POSIX-compatible argument quoting and parameter save/restore
# http://www.etalabs.net/sh_tricks.html
# Usage:
# parameters=$(save "$@") # save the original parameters.
# eval "set -- ${parameters}" # restore the saved parameters.
save () {
    local param
    for param; do
        printf %s\\n "$param" \
            | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
    done
    printf %s\\n " "
}

# Exit with status $1 after displaying error message $2.
exiterr () {
    printf %s\\n "$2" >&2
    exit $1
}

# POSIX-compatible command line option parsing.
# This function supports long options and optional arguments, and is
# a (largely-compatible) drop-in replacement for GNU getopt.
#
# Instead of:
# opts=$(getopt -o "$shortopts" -l "$longopts" -- "$@")
# eval set -- ${opts}
#
# We instead use:
# opts=$(posix_getopt "$shortopts" "$longopts" "$@")
# eval "set -- ${opts}"
posix_getopt () { # args: "$shortopts" "$longopts" "$@"
    local shortopts longopts \
          arg argtype getopt nonopt opt optchar optword suffix

    shortopts="$1"
    longopts="$2"
    shift 2

    getopt=
    nonopt=
    while [ $# -gt 0 ]; do
        opt=
        arg=
        argtype=
        case "$1" in
            # '--' means don't parse the remaining options
            ( -- ) {
                getopt="${getopt}$(save "$@")"
                shift $#
                break
            };;
            # process short option
            ( -[!-]* ) {         # -x[foo]
                suffix=${1#-?}   # foo
                opt=${1%$suffix} # -x
                optchar=${opt#-} # x
                case "${shortopts}" in
                    ( *${optchar}::* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *${optchar}:* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 "$1 requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 "$1 requires an argument";;
                            esac
                        fi
                    };;
                    ( *${optchar}* ) { # no argument
                        argtype=none
                        arg=
                        shift
                        # Handle multiple no-argument parameters combined as
                        # -xyz instead of -x -y -z. If we have just shifted
                        # parameter -xyz, we now replace it with -yz (which
                        # will be processed in the next iteration).
                        if [ -n "${suffix}" ]; then
                            eval "set -- $(save "-${suffix}")$(save "$@")"
                        fi
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # process long option
            ( --?* ) {            # --xarg[=foo]
                suffix=${1#*=}    # foo (unless there was no =)
                if [ "${suffix}" = "$1" ]; then
                    suffix=
                fi
                opt=${1%=$suffix} # --xarg
                optword=${opt#--} # xarg
                case ",${longopts}," in
                    ( *,${optword}::,* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *,${optword}:,* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 \
                                       "--${optword} requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 \
                                       "--${optword} requires an argument";;
                            esac
                        fi
                    };;
                    ( *,${optword},* ) { # no argument
                        if [ -n "${suffix}" ]; then
                            exiterr 1 "--${optword} does not take an argument"
                        fi
                        argtype=none
                        arg=
                        shift
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # any other parameters starting with -
            ( -* ) exiterr 1 "Unknown option $1";;
            # remember non-option parameters
            ( * ) nonopt="${nonopt}$(save "$1")"; shift;;
        esac

        if [ -n "${opt}" ]; then
            getopt="${getopt}$(save "$opt")"
            case "${argtype}" in
                ( optional|required ) {
                    getopt="${getopt}$(save "$arg")"
                };;
            esac
        fi
    done

    # Generate function output, suitable for:
    # eval "set -- $(posix_getopt ...)"
    printf %s "${getopt}"
    if [ -n "${nonopt}" ]; then
        printf %s "$(save "--")${nonopt}"
    fi
}

Exemple d'utilisation:

# Process command line options
shortopts="hvd:c::s::L:D"
longopts="help,version,directory:,client::,server::,load:,delete"
#opts=$(getopt -o "$shortopts" -l "$longopts" -n "$(basename <*>)" -- "$@")
opts=$(posix_getopt "$shortopts" "$longopts" "$@")
if [ $? -eq 0 ]; then
    #eval set -- ${opts}
    eval "set -- ${opts}"
    while [ $# -gt 0 ]; do
        case "$1" in
            ( --                ) shift; break;;
            ( -h|--help         ) help=1; shift; break;;
            ( -v|--version      ) version_help=1; shift; break;;
            ( -d|--directory    ) dir=$2; shift 2;;
            ( -c|--client       ) useclient=1; client=$2; shift 2;;
            ( -s|--server       ) startserver=1; server_name=$2; shift 2;;
            ( -L|--load         ) load=$2; shift 2;;
            ( -D|--delete       ) delete=1; shift;;
        esac
    done
else
    shorthelp=1 # getopt returned (and reported) an error.
fi

Je n'écris que de temps en temps des scripts shell et je ne pratique plus, donc tout commentaire est apprécié.

En utilisant la stratégie proposée par @Arvid Requate, nous avons remarqué quelques erreurs d'utilisateur. Un utilisateur qui oublie d'inclure une valeur aura accidentellement le nom de l'option suivante traité comme une valeur:

./getopts_test.sh --loglevel= --toc=TRUE

provoquera la valeur de " loglevel " être considéré comme "- toc = TRUE". Cela peut être évité.

J'ai adapté quelques idées sur la vérification des erreurs d'utilisateur pour l'interface de ligne de commande de http://mwiki.wooledge.org/BashFAQ. / 035 discussion sur l'analyse manuelle. J'ai inséré une erreur de vérification dans la gestion des deux "-" et ";" et " " arguments.

Ensuite, j'ai commencé à jouer avec la syntaxe, donc toutes les erreurs ici sont strictement de ma faute, pas les auteurs d'origine.

Mon approche aide les utilisateurs qui préfèrent entrer longtemps avec ou sans le signe égal. Autrement dit, il devrait avoir la même réponse à " - loglevel 9 " comme "- loglevel = 9". Dans la méthode - / space, il n'est pas possible de savoir avec certitude si l'utilisateur oublie un argument, il est donc nécessaire de deviner.

  1. si l'utilisateur a le format de signe long / égal (--opt =), un espace après = déclenche une erreur car aucun argument n'a été fourni.
  2. si l'utilisateur a des arguments longs / espace (--opt), ce script provoque un échec si aucun argument ne suit (fin de la commande) ou si l'argument commence par un tiret)

Si vous débutez dans ce domaine, il existe une différence intéressante entre " - opt = value " et " - opt value " formats. Avec le signe égal, l’argument de la ligne de commande est considéré comme "opt = value". et le travail à gérer qui consiste à analyser les chaînes, à séparer à "=". En revanche, avec "- opt value", le nom de l'argument est "opt". et nous avons le défi d'obtenir la prochaine valeur fournie dans la ligne de commande. C'est là que @Arvid Requate a utilisé $ {! OPTIND}, la référence indirecte. Je ne comprends toujours pas cela. Les commentaires dans BashFAQ semblent mettre en garde contre ce style ( http : //mywiki.wooledge.org/BashFAQ/006 ). BTW, je ne pense pas que les commentaires des précédentes affiches sur l'importance d'OPTIND = $ (($ OPTIND + 1)) soient corrects. Je veux dire, je ne vois pas de mal à l'omettre.

Dans la dernière version de ce script, l'indicateur -v signifie une impression VERBOSE.

Enregistrez-le dans un fichier nommé "cli-5.sh", make exécutable. Tous ces éléments fonctionneront ou échoueront de la manière souhaitée.

./cli-5.sh  -v --loglevel=44 --toc  TRUE
./cli-5.sh  -v --loglevel=44 --toc=TRUE
./cli-5.sh --loglevel 7
./cli-5.sh --loglevel=8
./cli-5.sh -l9

./cli-5.sh  --toc FALSE --loglevel=77
./cli-5.sh  --toc=FALSE --loglevel=77

./cli-5.sh   -l99 -t yyy
./cli-5.sh   -l 99 -t yyy

Voici un exemple de sortie du contrôle d'erreur sur l'utilisateur intpu

$ ./cli-5.sh  --toc --loglevel=77
ERROR: toc value must not have dash at beginning
$ ./cli-5.sh  --toc= --loglevel=77
ERROR: value for toc undefined

Vous devriez envisager d'activer -v, car il affiche les éléments internes d'OPTIND et d'OPTARG

#/usr/bin/env bash

## Paul Johnson
## 20171016
##

## Combines ideas from
## https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
## by @Arvid Requate, and http://mwiki.wooledge.org/BashFAQ/035

# What I don't understand yet: 
# In @Arvid REquate's answer, we have 
# val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
# this works, but I don't understand it!


die() {
    printf '%s\n' "$1" >&2
    exit 1
}

printparse(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
    fi
}

showme(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'VERBOSE: %s\n' "$1" >&2;
    fi
}


VERBOSE=0
loglevel=0
toc="TRUE"

optspec=":vhl:t:-:"
while getopts "$optspec" OPTCHAR; do

    showme "OPTARG:  ${OPTARG[*]}"
    showme "OPTIND:  ${OPTIND[*]}"
    case "${OPTCHAR}" in
        -)
            case "${OPTARG}" in
                loglevel) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) # CAUTION! no effect?
                    printparse "--${OPTARG}" "  " "${val}"
                    loglevel="${val}"
                    shift
                    ;;
                loglevel=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        printparse "--${opt}" "=" "${val}"
                        loglevel="${val}"
                        ## shift CAUTION don't shift this, fails othewise
                    else
                        die "ERROR: $opt value must be supplied"
                    fi
                    ;;
                toc) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) #??
                    printparse "--${opt}" " " "${val}"
                    toc="${val}"
                    shift
                    ;;
                toc=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        toc=${val}
                        printparse "--$opt" " -> " "$toc"
                        ##shift ## NO! dont shift this
                    else
                        die "ERROR: value for $opt undefined"
                    fi
                    ;;

                help)
                    echo "usage: <*> [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
                    exit 2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h|-\?|--help)
            ## must rewrite this for all of the arguments
            echo "usage: <*> [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
            exit 2
            ;;
        l)
            loglevel=${OPTARG}
            printparse "-l" " "  "${loglevel}"
            ;;
        t)
            toc=${OPTARG}
            ;;
        v)
            VERBOSE=1
            ;;

        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done



echo "
After Parsing values
"
echo "loglevel  $loglevel" 
echo "toc  $toc"

Vous trouverez ici quelques approches différentes pour l’analyse d’options complexes dans bash: http://mywiki.wooledge.org/ComplexOptionParsing

J'ai créé le suivant, et je pense que c'est un bon choix, car c'est un code minimal et les options longues et courtes fonctionnent. Une option longue peut également avoir plusieurs arguments avec cette approche.

#!/bin/bash
# Uses bash extensions.  Not portable as written.

declare -A longoptspec
longoptspec=( [loglevel]=1 ) #use associative array to declare how many arguments a long option expects, in this case we declare that loglevel expects/has one argument, long options that aren't listed i n this way will have zero arguments by default
optspec=":h-:"
while getopts "$optspec" opt; do
while true; do
    case "${opt}" in
        -) #OPTARG is name-of-long-option or name-of-long-option=value
            if [[ "${OPTARG}" =~ .*=.* ]] #with this --key=value format only one argument is possible
            then
                opt=${OPTARG/=*/}
                OPTARG=${OPTARG#*=}
                ((OPTIND--))    
            else #with this --key value1 value2 format multiple arguments are possible
                opt="$OPTARG"
                OPTARG=(${@:OPTIND:$((longoptspec[$opt]))})
            fi
            ((OPTIND+=longoptspec[$opt]))
            continue #now that opt/OPTARG are set we can process them as if getopts would've given us long options
            ;;
        loglevel)
          loglevel=$OPTARG
            ;;
        h|help)
            echo "usage: <*> [--loglevel[=]<value>]" >&2
            exit 2
            ;;
    esac
break; done
done

# End of file

Je travaille sur ce sujet depuis assez longtemps ... et j'ai créé ma propre bibliothèque que vous devrez identifier dans votre script principal. Voir libopt4shell et cd2mpc pour un exemple. J'espère que ça aide!

Une solution améliorée:

# translate long options to short
# Note: This enable long options but disable "--?*" in $OPTARG, or disable long options after  "--" in option fields.
for ((i=1;$#;i++)) ; do
    case "$1" in
        --)
            # [ ${args[$((i-1))]} == ... ] || EndOpt=1 ;;& # DIRTY: we still can handle some execptions...
            EndOpt=1 ;;&
        --version) ((EndOpt)) && args[$i]="$1"  || args[$i]="-V";;
        # default case : short option use the first char of the long option:
        --?*) ((EndOpt)) && args[$i]="$1"  || args[$i]="-${1:2:1}";;
        # pass through anything else:
        *) args[$i]="$1" ;;
    esac
    shift
done
# reset the translated args
set -- "${args[@]}"

function usage {
echo "Usage: <*> [options] files" >&2
    exit $1
}

# now we can process with getopt
while getopts ":hvVc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        V)  echo $Version ; exit ;;
        c)  source $OPTARG ;;
        \?) echo "unrecognized option: -$opt" ; usage -1 ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage -1
        ;;
    esac
done

shift $((OPTIND-1))
[[ "$1" == "--" ]] && shift

Peut-être qu'il est plus simple d'utiliser ksh, juste pour la partie getopts, si besoin de longues options de ligne de commande, car cela peut être plus facile à faire là-bas.

# Working Getopts Long => KSH

#! /bin/ksh
# Getopts Long
USAGE="s(showconfig)"
USAGE+="c:(createdb)"
USAGE+="l:(createlistener)"
USAGE+="g:(generatescripts)"
USAGE+="r:(removedb)"
USAGE+="x:(removelistener)"
USAGE+="t:(createtemplate)"
USAGE+="h(help)"

while getopts "$USAGE" optchar ; do
    case $optchar in
    s)  echo "Displaying Configuration" ;;
        c)  echo "Creating Database $OPTARG" ;;
    l)  echo "Creating Listener LISTENER_$OPTARG" ;;
    g)  echo "Generating Scripts for Database $OPTARG" ;;
    r)  echo "Removing Database $OPTARG" ;;
    x)  echo "Removing Listener LISTENER_$OPTARG" ;;
    t)  echo "Creating Database Template" ;;
    h)  echo "Help" ;;
    esac
done

Je n'ai pas encore assez de représentants pour commenter ou voter sa solution, mais la réponse de pme a très bien fonctionné . moi. Le seul problème que j'ai rencontré est que les arguments finissent par être entourés de guillemets simples (je les ai donc supprimés).

J'ai également ajouté des exemples d'utilisation et du texte HELP. Je vais inclure ma version légèrement étendue ici:

#!/bin/bash

# getopt example
# from: https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
HELP_TEXT=\
"   USAGE:\n
    Accepts - and -- flags, can specify options that require a value, and can be in any order. A double-hyphen (--) will stop processing options.\n\n

    Accepts the following forms:\n\n

    getopt-example.sh -a -b -c value-for-c some-arg\n
    getopt-example.sh -c value-for-c -a -b some-arg\n
    getopt-example.sh -abc some-arg\n
    getopt-example.sh --along --blong --clong value-for-c -a -b -c some-arg\n
    getopt-example.sh some-arg --clong value-for-c\n
    getopt-example.sh
"

aflag=false
bflag=false
cargument=""

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc:h\? -l along,blong,help,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag=true ;;
    -b|--blong) bflag=true ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    -h|--help|-\?) echo -e $HELP_TEXT; exit;;
    (--) shift; break;;
    (-*) echo "<*>: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done

# to remove the single quotes around arguments, pipe the output into:
# | sed -e "s/^'\\|'$//g"  (just leading/trailing) or | sed -e "s/'//g"  (all)

echo aflag=${aflag}
echo bflag=${bflag}
echo cargument=${cargument}

while [ $# -gt 0 ]
do
    echo arg=$1
    shift

    if [[ $aflag == true ]]; then
        echo a is true
    fi

done

Je voulais quelque chose sans dépendances externes, avec un support strict de bash (-u), et j’avais besoin que cela fonctionne même sur les anciennes versions de bash. Ceci gère différents types de paramètres:

  • courtes bools (-h)
  • options courtes (-i "quot.image.jpg")
  • long bools (--help)
  • est égal à options (--file = "filename.ext")
  • options d'espace (--file " nomfichier.ext ")
  • objets concaténés (-hvm)

Il suffit d'insérer les éléments suivants en haut de votre script:

# Check if a list of params contains a specific param
# usage: if _param_variant "h|?|help p|path f|file long-thing t|test-thing" "file" ; then ...
# the global variable $key is updated to the long notation (last entry in the pipe delineated list, if applicable)
_param_variant() {
  for param in $1 ; do
    local variants=${param//\|/ }
    for variant in $variants ; do
      if [[ "$variant" = "$2" ]] ; then
        # Update the key to match the long version
        local arr=(${param//\|/ })
        let last=${#arr[@]}-1
        key="${arr[$last]}"
        return 0
      fi
    done
  done
  return 1
}

# Get input parameters in short or long notation, with no dependencies beyond bash
# usage:
#     # First, set your defaults
#     param_help=false
#     param_path="."
#     param_file=false
#     param_image=false
#     param_image_lossy=true
#     # Define allowed parameters
#     allowed_params="h|?|help p|path f|file i|image image-lossy"
#     # Get parameters from the arguments provided
#     _get_params $*
#
# Parameters will be converted into safe variable names like:
#     param_help,
#     param_path,
#     param_file,
#     param_image,
#     param_image_lossy
#
# Parameters without a value like "-h" or "--help" will be treated as
# boolean, and will be set as param_help=true
#
# Parameters can accept values in the various typical ways:
#     -i "path/goes/here"
#     --image "path/goes/here"
#     --image="path/goes/here"
#     --image=path/goes/here
# These would all result in effectively the same thing:
#     param_image="path/goes/here"
#
# Concatinated short parameters (boolean) are also supported
#     -vhm is the same as -v -h -m
_get_params(){

  local param_pair
  local key
  local value
  local shift_count

  while : ; do
    # Ensure we have a valid param. Allows this to work even in -u mode.
    if [[ $# == 0 || -z $1 ]] ; then
      break
    fi

    # Split the argument if it contains "="
    param_pair=(${1//=/ })
    # Remove preceeding dashes
    key="${param_pair[0]#--}"

    # Check for concatinated boolean short parameters.
    local nodash="${key#-}"
    local breakout=false
    if [[ "$nodash" != "$key" && ${#nodash} -gt 1 ]]; then
      # Extrapolate multiple boolean keys in single dash notation. ie. "-vmh" should translate to: "-v -m -h"
      local short_param_count=${#nodash}
      let new_arg_count=$#+$short_param_count-1
      local new_args=""
      # $str_pos is the current position in the short param string $nodash
      for (( str_pos=0; str_pos<new_arg_count; str_pos++ )); do
        # The first character becomes the current key
        if [ $str_pos -eq 0 ] ; then
          key="${nodash:$str_pos:1}"
          breakout=true
        fi
        # $arg_pos is the current position in the constructed arguments list
        let arg_pos=$str_pos+1
        if [ $arg_pos -gt $short_param_count ] ; then
          # handle other arguments
          let orignal_arg_number=$arg_pos-$short_param_count+1
          local new_arg="${!orignal_arg_number}"
        else
          # break out our one argument into new ones
          local new_arg="-${nodash:$str_pos:1}"
        fi
        new_args="$new_args \"$new_arg\""
      done
      # remove the preceding space and set the new arguments
      eval set -- "${new_args# }"
    fi
    if ! $breakout ; then
      key="$nodash"
    fi

    # By default we expect to shift one argument at a time
    shift_count=1
    if [ "${#param_pair[@]}" -gt "1" ] ; then
      # This is a param with equals notation
      value="${param_pair[1]}"
    else
      # This is either a boolean param and there is no value,
      # or the value is the next command line argument
      # Assume the value is a boolean true, unless the next argument is found to be a value.
      value=true
      if [[ $# -gt 1 && -n "$2" ]]; then
        local nodash="${2#-}"
        if [ "$nodash" = "$2" ]; then
          # The next argument has NO preceding dash so it is a value
          value="$2"
          shift_count=2
        fi
      fi
    fi

    # Check that the param being passed is one of the allowed params
    if _param_variant "$allowed_params" "$key" ; then
      # --key-name will now become param_key_name
      eval param_${key//-/_}="$value"
    else
      printf 'WARNING: Unknown option (ignored): %s\n' "$1" >&2
    fi
    shift $shift_count
  done
}

Et utilisez-le comme suit:

# Assign defaults for parameters
param_help=false
param_path=$(pwd)
param_file=false
param_image=true
param_image_lossy=true
param_image_lossy_quality=85

# Define the params we will allow
allowed_params="h|?|help p|path f|file i|image image-lossy image-lossy-quality"

# Get the params from arguments provided
_get_params $*

La réponse acceptée fait très bien ressortir toutes les lacunes de getopts intégré dans bash. La réponse se termine par:

  

Ainsi, s’il est possible d’écrire plus de code pour pallier le manque de prise en charge des options longues, c’est beaucoup plus de travail et annule en partie le but de l’utilisation d’un analyseur getopt pour simplifier votre code.

Et même si je souscris en principe à cette affirmation, j'estime que le nombre de fois où nous avons tous implémenté cette fonctionnalité dans divers scripts justifie de déployer des efforts pour créer une solution "normalisée", bien testée.

En tant que tel, j'ai "mis à niveau" et bash intégré dans getopts en implémentant getopts_long en bash pur, sans dépendance externe. L'utilisation de la fonction est compatible à 100% avec le getopts intégré.

En incluant getopts_long (hébergé sur GitHub à l'adresse https: // github. com / UmkaDK / getopts_long ) dans un script, la réponse à la question d'origine peut être implémentée aussi simplement que:

source "${PATH_TO}/getopts_long.bash"

while getopts_long ':c: copyfile:' OPTKEY; do
    case ${OPTKEY} in
        'c'|'copyfile')
            echo 'file supplied -- ${OPTARG}'
            ;;
        '?')
            echo "INVALID OPTION -- ${OPTARG}" >&2
            exit 1
            ;;
        ':')
            echo "MISSING ARGUMENT for option -- ${OPTARG}" >&2
            exit 1
            ;;
        *)
            echo "Misconfigured OPTSPEC or uncaught option -- ${OPTKEY}" >&2
            exit 1
            ;;
    esac
done

shift $(( OPTIND - 1 ))
[[ "${1}" == "--" ]] && shift

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

ArgParser::addArg "[h]elp"    false    "This list"
ArgParser::addArg "[q]uiet"   false    "Supress output"
ArgParser::addArg "[s]leep"   1        "Seconds to sleep"
ArgParser::addArg "v"         1        "Verbose mode"

ArgParser::parse "$@"

ArgParser::isset help && ArgParser::showArgs

ArgParser::isset "quiet" \
   && echo "Quiet!" \
   || echo "Noisy!"

local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
   && echo "Sleep for 

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

#!/usr/bin/env bash

# Updates to this script may be found at
# http://nt4.com/bash/argparser.inc.sh

# Example of runtime usage:
# mnc.sh --nc -q Caprica.S0*mkv *.avi *.mp3 --more-options here --host centos8.host.com

# Example of use in script (see bottom)
# Just include this file in yours, or use
# source argparser.inc.sh

unset EXPLODED
declare -a EXPLODED
function explode 
{
    local c=$# 
    (( c < 2 )) && 
    {
        echo function "<*>" is missing parameters 
        return 1
    }

    local delimiter="$1"
    local string="$2"
    local limit=${3-99}

    local tmp_delim=

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_sleep seconds" \ || echo "No value passed for sleep" # This way is often more convienient, but is a little slower echo "Sleep set to: $( ArgParser::getArg sleep )"

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>\x07' local delin=${string//$delimiter/$tmp_delim} local oldifs="$IFS" IFS="$tmp_delim" EXPLODED=($delin) IFS="$oldifs" } # See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference # Usage: local "$1" && upvar $1 "value(s)" upvar() { if unset -v "$1"; then # Unset & validate varname if (( $# == 2 )); then eval $1=\"\$2\" # Return single value else eval $1=\(\"\${@:2}\"\) # Return array fi fi } function decho { : } function ArgParser::check { __args=${#__argparser__arglist[@]} for (( i=0; i<__args; i++ )) do matched=0 explode "|" "${__argparser__arglist[$i]}" if [ "${#1}" -eq 1 ] then if [ "${1}" == "${EXPLODED[0]}" ] then decho "Matched $1 with ${EXPLODED[0]}" matched=1 break fi else if [ "${1}" == "${EXPLODED[1]}" ] then decho "Matched $1 with ${EXPLODED[1]}" matched=1 break fi fi done (( matched == 0 )) && return 2 # decho "Key $key has default argument of ${EXPLODED[3]}" if [ "${EXPLODED[3]}" == "false" ] then return 0 else return 1 fi } function ArgParser::set { key=$3 value="${1:-true}" declare -g __argpassed__$key="$value" } function ArgParser::parse { unset __argparser__argv __argparser__argv=() # echo parsing: "$@" while [ -n "$1" ] do # echo "Processing $1" if [ "${1:0:2}" == '--' ] then key=${1:2} value=$2 elif [ "${1:0:1}" == '-' ] then key=${1:1} # Strip off leading - value=$2 else decho "Not argument or option: '$1'" >& 2 __argparser__argv+=( "$1" ) shift continue fi # parameter=${tmp%%=*} # Extract name. # value=${tmp##*=} # Extract value. decho "Key: '$key', value: '$value'" # eval $parameter=$value ArgParser::check $key el=$? # echo "Check returned $el for $key" [ $el -eq 2 ] && decho "No match for option '$1'" >&2 # && __argparser__argv+=( "$1" ) [ $el -eq 0 ] && decho "Matched option '${EXPLODED[2]}' with no arguments" >&2 && ArgParser::set true "${EXPLODED[@]}" [ $el -eq 1 ] && decho "Matched option '${EXPLODED[2]}' with an argument of '$2'" >&2 && ArgParser::set "$2" "${EXPLODED[@]}" && shift shift done } function ArgParser::isset { declare -p "__argpassed__$1" > /dev/null 2>&1 && return 0 return 1 } function ArgParser::getArg { # This one would be a bit silly, since we can only return non-integer arguments ineffeciently varname="__argpassed__$1" echo "${!varname}" } ## # usage: tryAndGetArg <argname> into <varname> # returns: 0 on success, 1 on failure function ArgParser::tryAndGetArg { local __varname="__argpassed__$1" local __value="${!__varname}" test -z "

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

ArgParser::addArg "[h]elp"    false    "This list"
ArgParser::addArg "[q]uiet"   false    "Supress output"
ArgParser::addArg "[s]leep"   1        "Seconds to sleep"
ArgParser::addArg "v"         1        "Verbose mode"

ArgParser::parse "$@"

ArgParser::isset help && ArgParser::showArgs

ArgParser::isset "quiet" \
   && echo "Quiet!" \
   || echo "Noisy!"

local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
   && echo "Sleep for 

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

<*>

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_sleep seconds" \ || echo "No value passed for sleep" # This way is often more convienient, but is a little slower echo "Sleep set to: $( ArgParser::getArg sleep )"

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_value" && return 1 local "$3" && upvar $3 "

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

ArgParser::addArg "[h]elp"    false    "This list"
ArgParser::addArg "[q]uiet"   false    "Supress output"
ArgParser::addArg "[s]leep"   1        "Seconds to sleep"
ArgParser::addArg "v"         1        "Verbose mode"

ArgParser::parse "$@"

ArgParser::isset help && ArgParser::showArgs

ArgParser::isset "quiet" \
   && echo "Quiet!" \
   || echo "Noisy!"

local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
   && echo "Sleep for 

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

<*>

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_sleep seconds" \ || echo "No value passed for sleep" # This way is often more convienient, but is a little slower echo "Sleep set to: $( ArgParser::getArg sleep )"

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_value" return 0 } function ArgParser::__construct { unset __argparser__arglist # declare -a __argparser__arglist } ## # @brief add command line argument # @param 1 short and/or long, eg: [s]hort # @param 2 default value # @param 3 description ## function ArgParser::addArg { # check for short arg within long arg if [[ "$1" =~ \[(.)\] ]] then short=${BASH_REMATCH[1]} long=${1/\[$short\]/$short} else long=$1 fi if [ "${#long}" -eq 1 ] then short=$long long='' fi decho short: "$short" decho long: "$long" __argparser__arglist+=("$short|$long|$1|$2|$3") } ## # @brief show available command line arguments ## function ArgParser::showArgs { # declare -p | grep argparser printf "Usage: %s [OPTION...]\n\n" "$( basename "${BASH_SOURCE[0]}" )" printf "Defaults for the options are specified in brackets.\n\n"; __args=${#__argparser__arglist[@]} for (( i=0; i<__args; i++ )) do local shortname= local fullname= local default= local description= local comma= explode "|" "${__argparser__arglist[$i]}" shortname="${EXPLODED[0]:+-${EXPLODED[0]}}" # String Substitution Guide: fullname="${EXPLODED[1]:+--${EXPLODED[1]}}" # http://tldp.org/LDP/abs/html/parameter-substitution.html test -n "$shortname" \ && test -n "$fullname" \ && comma="," default="${EXPLODED[3]}" case $default in false ) default= ;; "" ) default= ;; * ) default="[$default]" esac description="${EXPLODED[4]}" printf " %2s%1s %-19s %s %s\n" "$shortname" "$comma" "$fullname" "$description" "$default" done } function ArgParser::test { # Arguments with a default of 'false' do not take paramaters (note: default # values are not applied in this release) ArgParser::addArg "[h]elp" false "This list" ArgParser::addArg "[q]uiet" false "Supress output" ArgParser::addArg "[s]leep" 1 "Seconds to sleep" ArgParser::addArg "v" 1 "Verbose mode" ArgParser::parse "$@" ArgParser::isset help && ArgParser::showArgs ArgParser::isset "quiet" \ && echo "Quiet!" \ || echo "Noisy!" local __sleep ArgParser::tryAndGetArg sleep into __sleep \ && echo "Sleep for

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

ArgParser::addArg "[h]elp"    false    "This list"
ArgParser::addArg "[q]uiet"   false    "Supress output"
ArgParser::addArg "[s]leep"   1        "Seconds to sleep"
ArgParser::addArg "v"         1        "Verbose mode"

ArgParser::parse "$@"

ArgParser::isset help && ArgParser::showArgs

ArgParser::isset "quiet" \
   && echo "Quiet!" \
   || echo "Noisy!"

local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
   && echo "Sleep for 

Afin de rester compatible entre plates-formes et d'éviter de dépendre des exécutables externes, j'ai transféré du code provenant d'une autre langue.

Je trouve cela très facile à utiliser, voici un exemple:

<*>

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_sleep seconds" \ || echo "No value passed for sleep" # This way is often more convienient, but is a little slower echo "Sleep set to: $( ArgParser::getArg sleep )"

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_sleep seconds" \ || echo "No value passed for sleep" # This way is often more convienient, but is a little slower echo "Sleep set to: $( ArgParser::getArg sleep )" echo "Remaining command line: ${__argparser__argv[@]}" } if [ "$( basename "<*>" )" == "argparser.inc.sh" ] then ArgParser::test "$@" fi

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>_sleep seconds" \ || echo "No value passed for sleep" # This way is often more convienient, but is a little slower echo "Sleep set to: $( ArgParser::getArg sleep )"

Le BASH requis est un peu plus long qu'il ne pourrait l'être, mais je voulais éviter de dépendre des tableaux associatifs de BASH 4. Vous pouvez également le télécharger directement à partir de http://nt4.com/bash/argparser.inc.sh

<*>

Si toutes vos options longues ont des premiers caractères uniques et correspondants, tels que les options courtes, par exemple

./slamm --chaos 23 --plenty test -quiet

est identique à

./slamm -c 23 -p test -q

Vous pouvez utiliser cet avant getopts pour réécrire $ args:

# change long options to short options

for arg; do 
    [[ "${arg:0:1}" == "-" ]] && delim="" || delim="\""
    if [ "${arg:0:2}" == "--" ]; 
       then args="${args} -${arg:2:1}" 
       else args="${args} ${delim}${arg}${delim}"
    fi
done

# reset the incoming args
eval set -- $args

# proceed as usual
while getopts ":b:la:h" OPTION; do
    .....

Merci pour votre inspiration pour l'inspiration ;-)

getopts intégré ne traite que les options abrégées (sauf dans ksh93), mais vous pouvez toujours ajouter quelques lignes de script pour que getopts gère les options longues.

Voici une partie du code présent dans http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts

  #== set short options ==#
SCRIPT_OPTS=':fbF:B:-:h'
  #== set long options associated with short one ==#
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
    [foo]=f
    [bar]=b
    [foobar]=F
    [barfoo]=B
    [help]=h
    [man]=h
)

  #== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
    #== translate long options to short ==#
    if [[ "x$OPTION" == "x-" ]]; then
        LONG_OPTION=$OPTARG
        LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2)
        LONG_OPTIND=-1
        [[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1)
        [[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="\$LONG_OPTIND"
        OPTION=${ARRAY_OPTS[$LONG_OPTION]}
        [[ "x$OPTION" = "x" ]] &&  OPTION="?" OPTARG="-$LONG_OPTION"

        if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
            if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then 
                OPTION=":" OPTARG="-$LONG_OPTION"
            else
                OPTARG="$LONG_OPTARG";
                if [[ $LONG_OPTIND -ne -1 ]]; then
                    [[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
                    shift $OPTIND
                    OPTIND=1
                fi
            fi
        fi
    fi

    #== options follow by another option instead of argument ==#
    if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then 
        OPTARG="$OPTION" OPTION=":"
    fi

    #== manage options ==#
    case "$OPTION" in
        f  ) foo=1 bar=0                    ;;
        b  ) foo=0 bar=1                    ;;
        B  ) barfoo=${OPTARG}               ;;
        F  ) foobar=1 && foobar_name=${OPTARG} ;;
        h ) usagefull && exit 0 ;;
        : ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
        ? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
    esac
done
shift $((${OPTIND} - 1))

Voici un test:

# Short options test
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2

# Long and short options test
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2

Sinon, dans une version récente de Korn Shell , ksh93, getopts peut naturellement analyser de longues options et même afficher une page de manuel de la même manière. (Voir http: // www .uxora.com / unix / shell-script / 20-getopts-with-man-page-options-longues )

Le getopt intégré à OS X (BSD) ne prend pas en charge les options longues, mais la version GNU le fait: installez gnu-getopt . Ensuite, quelque chose de similaire à: cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt / usr / local / bin / gnu-getopt .

EasyOptions gère les options courtes et longues:

## Options:
##   --verbose, -v   Verbose mode
##   --logfile=NAME  Log filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "log file: ${logfile}"
    echo "arguments: ${arguments[@]}"
fi

Un bricolage simple pour obtenir uniquement des arguments nommés longs:

Utiliser:

$ ./test-args.sh --a1 a1 --a2 "a 2" --a3 --a4= --a5=a5 --a6="a 6"
a1 = "a1"
a2 = "a 2"
a3 = "TRUE"
a4 = ""
a5 = "a5"
a6 = "a 6"
a7 = ""

Script:

#!/bin/bash

function main() {
    ARGS=`getArgs "$@"`

    a1=`echo "$ARGS" | getNamedArg a1`
    a2=`echo "$ARGS" | getNamedArg a2`
    a3=`echo "$ARGS" | getNamedArg a3`
    a4=`echo "$ARGS" | getNamedArg a4`
    a5=`echo "$ARGS" | getNamedArg a5`
    a6=`echo "$ARGS" | getNamedArg a6`
    a7=`echo "$ARGS" | getNamedArg a7`

    echo "a1 = \"$a1\""
    echo "a2 = \"$a2\""
    echo "a3 = \"$a3\""
    echo "a4 = \"$a4\""
    echo "a5 = \"$a5\""
    echo "a6 = \"$a6\""
    echo "a7 = \"$a7\""

    exit 0
}


function getArgs() {
    for arg in "$@"; do
        echo "$arg"
    done
}


function getNamedArg() {
    ARG_NAME=$1

    sed --regexp-extended --quiet --expression="
        s/^--$ARG_NAME=(.*)\$/\1/p  # Get arguments in format '--arg=value': [s]ubstitute '--arg=value' by 'value', and [p]rint
        /^--$ARG_NAME\$/ {          # Get arguments in format '--arg value' ou '--arg'
            n                       # - [n]ext, because in this format, if value exists, it will be the next argument
            /^--/! p                # - If next doesn't starts with '--', it is the value of the actual argument
            /^--/ {                 # - If next do starts with '--', it is the next argument and the actual argument is a boolean one
                # Then just repla[c]ed by TRUE
                c TRUE
            }
        }
    "
}


main "$@"

si c'est simplement comme cela que vous voulez appeler le script

myscript.sh --input1 "ABC" --input2 "PQR" --input2 "XYZ"

alors vous pouvez suivre ce moyen le plus simple pour y parvenir à l'aide de getopt et de --longoptions

essayez ceci, espérons que cela soit utile

# Read command line options
ARGUMENT_LIST=(
    "input1"
    "input2"
    "input3"
)



# read arguments
opts=$(getopt \
    --longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")" \
    --name "$(basename "<*>")" \
    --options "" \
    -- "$@"
)


echo $opts

eval set --$opts

while true; do
    case "$1" in
    --input1)  
        shift
        empId=$1
        ;;
    --input2)  
        shift
        fromDate=$1
        ;;
    --input3)  
        shift
        toDate=$1
        ;;
      --)
        shift
        break
        ;;
    esac
    shift
done
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top