Pregunta

Deseo que se invocen formas largas y cortas de opciones de línea de comandos usando mi script de shell.

Sé que se pueden usar getopts , pero como en Perl, no he podido hacer lo mismo con Shell.

Cualquier idea sobre cómo se puede hacer esto, para que pueda usar opciones como:

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

En lo anterior, ambos comandos significan lo mismo para mi shell, pero usando getopts , ¿no he podido implementarlos?

¿Fue útil?

Solución

Hay tres implementaciones que pueden considerarse:

  • Bash builtin getopts . Esto no admite nombres de opción largos con el prefijo de doble guión. Solo admite opciones de un solo carácter.

  • Implementación BSD UNIX del comando getopt independiente (que es lo que usa MacOS). Esto tampoco admite opciones largas.

  • Implementación de GNU de getopt independiente. GNU getopt (3) (utilizado por la línea de comandos getopt (1) en Linux) admite el análisis de opciones largas.


Algunas otras respuestas muestran una solución para utilizar el getopts incorporado de bash para imitar opciones largas. Esa solución realmente hace una opción corta cuyo carácter es " - " ;. Entonces obtienes " - " como la bandera Luego, todo lo que sigue se convierte en OPTARG, y prueba el OPTARG con un case anidado.

Esto es inteligente, pero viene con advertencias:

  • getopts no puede imponer la especificación de opción. No puede devolver errores si el usuario proporciona una opción no válida. Debe realizar su propia comprobación de errores al analizar OPTARG.
  • OPTARG se usa para el nombre largo de la opción, lo que complica el uso cuando su propia opción larga tiene un argumento. Terminarás teniendo que codificar eso como un caso adicional.

Entonces, si bien es posible escribir más código para evitar la falta de soporte para opciones largas, es mucho más trabajo y anula parcialmente el propósito de usar un analizador getopt para simplificar su código.

Otros consejos

getopt y getopts son diferentes bestias, y las personas parecen tener un poco de incomprensión de lo que hacen. getopts es un comando incorporado para bash para procesar las opciones de la línea de comandos en un bucle y asignar cada opción y valor encontrados a las variables incorporadas, para que pueda Además procesarlos. getopt , sin embargo, es un programa de utilidad externo, y en realidad no procesa sus opciones para usted de la manera que, p. bash getopts , el módulo Perl Getopt o los módulos Python optparse / argparse hacen. Todo lo que getopt hace es canonicalizar las opciones que se pasan en & # 8212; es decir, conviértalos a una forma más estándar, para que sea más fácil que un script de shell los procese. Por ejemplo, una aplicación de getopt podría convertir lo siguiente:

myscript -ab infile.txt -ooutfile.txt

en esto:

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

Tienes que hacer el procesamiento real tú mismo. No tiene que usar getopt en absoluto si establece varias restricciones en la forma en que puede especificar opciones:

  • solo pone una opción por argumento;
  • todas las opciones van antes que cualquier parámetro posicional (es decir, argumentos sin opciones);
  • para opciones con valores (por ejemplo, -o arriba), el valor debe ir como un argumento separado (después de un espacio).

¿Por qué usar getopt en lugar de getopts ? La razón básica es que solo GNU getopt le brinda soporte para las opciones de línea de comandos de nombre largo. 1 (GNU getopt es el valor predeterminado en Linux Mac OS X y FreeBSD vienen con un getopt básico y no muy útil, pero la versión GNU se puede instalar; ver más abajo.

Por ejemplo, aquí hay un ejemplo del uso de GNU getopt , de un script mío llamado 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

Esto le permite especificar opciones como --verbose -dm4096 --minh = 20 --maxhe 40 --debugfi = " / Users / John Johnson / debug.txt " o similar. El efecto de la llamada a getopt es canonicalizar las opciones para --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile " / Users / John Johnson / debug.txt " para que pueda procesarlos más fácilmente. La cita alrededor de " $ 1 " y " $ 2 " es importante ya que garantiza que los argumentos con espacios en ellos se manejen correctamente.

¡Si elimina las primeras 9 líneas (todo a través de la línea eval set ), el código seguirá funcionando ! Sin embargo, su código será mucho más selectivo en cuanto a qué tipo de opciones acepta: en particular, tendrá que especificar todas las opciones en "canónico". formulario descrito anteriormente. Sin embargo, con el uso de getopt , puede agrupar opciones de una sola letra, usar formas más cortas y no ambiguas de opciones largas, usar --file foo.txt o el estilo --file = foo.txt , use el estilo -m 4096 o -m4096 , mezcle opciones y no opciones en cualquier orden, etc. getopt también genera un mensaje de error si se encuentran opciones no reconocidas o ambiguas.

NOTA : en realidad, hay dos versiones totalmente diferentes de getopt , getopt y GNU getopt , con diferentes características y diferentes convenciones de llamadas. 2 Básico getopt está bastante roto: no solo no maneja opciones largas, sino que ni siquiera puede maneja espacios incrustados dentro de argumentos o argumentos vacíos, mientras que getopts hace esto correctamente. El código anterior no funcionará en getopt básico. GNU getopt se instala por defecto en Linux, pero en Mac OS X y FreeBSD necesita instalarse por separado. En Mac OS X, instale MacPorts ( http://www.macports.org ) y luego haga sudo port install getopt para instalar GNU getopt (generalmente en / opt / local / bin ) y asegúrese de que / opt / local / bin esté en su ruta de acceso de shell delante de / usr / bin . En FreeBSD, instale misc / getopt .

Una guía rápida para modificar el código de ejemplo para su propio programa: de las primeras líneas, todo es "boilerplate". eso debería permanecer igual, excepto la línea que llama a getopt . Debe cambiar el nombre del programa después de -n , especificar opciones cortas después de -o , y opciones largas después de --long . Poner dos puntos después de las opciones que toman un valor.

Finalmente, si ve un código que solo tiene set en lugar de eval set , fue escrito para BSD getopt . Debe cambiarlo para usar el estilo eval set , que funciona bien con ambas versiones de getopt , mientras que el set simple no funciona correctamente con GNU getopt .

1 En realidad, getopts en ksh93 admite opciones de nombre largo, pero este shell no se usa con tanta frecuencia como bash . En zsh , use zparseopts para obtener esta funcionalidad.

2 Técnicamente, " GNU getopt " es un nombre inapropiado; Esta versión fue escrita para Linux en lugar del proyecto GNU. Sin embargo, sigue todas las convenciones de GNU y el término " GNU getopt " se usa comúnmente (por ejemplo, en FreeBSD).

La función incorporada Bop getopts se puede usar para analizar opciones largas al poner un carácter de guión seguido de dos puntos en 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

Después de copiar al archivo ejecutable nombre = getopts_test.sh en el directorio de trabajo actual , uno puede producir resultados como

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

Obviamente, getopts no realiza la verificación OPTERR ni el análisis de argumentos de opción para las opciones largas. El fragmento de script anterior muestra cómo se puede hacer esto manualmente. El principio básico también funciona en el shell de Debian Almquist (" dash "). Tenga en cuenta el caso especial:

<*>

Tenga en cuenta que, como GreyCat de más de http://mywiki.wooledge.org/BashFAQ puntos fuera, este truco explota un comportamiento no estándar del shell que permite que la opción-argumento (es decir, el nombre de archivo en " -f filename ") se concatene a la opción (como en " -ffilename "). El estándar POSIX dice que debe haber un espacio entre ellos, que en el caso de " - longoption " terminaría el análisis de opciones y convertiría todas las opciones en argumentos no opcionales.

El comando incorporado getopts todavía está, AFAIK, limitado a las opciones de un solo carácter.

Existe (o solía ser) un programa externo getopt que reorganizaría un conjunto de opciones para que fuera más fácil de analizar. También puede adaptar ese diseño para manejar opciones largas. Ejemplo de uso:

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
...

Podría usar un esquema similar con un comando getoptlong .

Tenga en cuenta que la debilidad fundamental con el programa externo getopt es la dificultad de manejar los argumentos con espacios en ellos y de preservar esos espacios con precisión. Esta es la razón por la cual el getopts incorporado es superior, aunque limitado por el hecho de que solo maneja las opciones de una sola letra.

Este es un ejemplo que realmente usa getopt con opciones largas:

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

Las opciones largas se pueden analizar mediante el getopts incorporado como "argumentos" a la - "opción"

Este es un shell POSIX portátil y nativo: no se necesitan programas externos ni bashismos.

Esta guía implementa opciones largas como argumentos para la opción - , por lo que getopts ve - con el argumento alpha y --bravo = foo se ve como - con el argumento bravo = foo . El verdadero argumento se puede cosechar con un simple reemplazo: $ {OPTARG # * =} .

En este ejemplo, -b (y su forma larga, --bravo ) tiene una opción obligatoria (tenga en cuenta la reconstrucción manual de hacer cumplir eso para la forma larga) . Las opciones no booleanas para argumentos largos vienen después de signos de igual, por ejemplo, --bravo = foo (los delimitadores de espacio para opciones largas serían difíciles de implementar).

Debido a que utiliza getopts , esta solución admite el uso como cmd -ac --bravo = foo -d FILE (que tiene opciones combinadas -a y - c y entrelaza opciones largas con opciones estándar) mientras que la mayoría de las otras respuestas aquí tienen dificultades o no lo hacen.

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

Cuando el argumento es un guión ( - ), tiene dos componentes más: el nombre del indicador y (opcionalmente) su argumento. Los delimito como lo haría cualquier comando, con el primer signo igual ( = ). $ LONG_OPTARG es, por lo tanto, simplemente el contenido de $ OPTARG sin el nombre del indicador o el signo igual.

El caso interno implementa opciones largas manualmente, por lo que necesita un poco de mantenimiento:

  • bravo =? coincide con --bravo = foo pero no --bravo = (nota: caso se detiene después del primer partido)
  • A continuación se muestra
  • bravo * , observando el argumento necesario que falta en --bravo y --bravo=
  • alfa * | charlie * captura los argumentos dados a las opciones que no los admiten
  • '' está presente para admitir las no opciones que comienzan con guiones
  • * captura todas las demás opciones largas y recrea el error arrojado por getopts para una opción no válida

No necesariamente necesitas todos esos artículos de limpieza. Por ejemplo, quizás desee que --bravo tenga un argumento opcional (que -b no puede admitir debido a una limitación en getopts ). Simplemente elimine el =? y el caso de falla relacionado y luego llame a $ {ARG_B: = $ DEFAULT_ARG_B} la primera vez que use $ ARG_B .


Para aceptar opciones largas con argumentos delimitados por espacios, necesitará un eval :

(decentemente seguro)
           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 ;;

Esta cláusula adicional, agregada directamente después de la versión de asignación = , ejecuta un eval para interpretar el valor del parámetro luego de la opción que se está interpretando en ese momento. Bash puede hacer esto de manera más limpia usando expansión indirecta , ARG_B = " $ {! OPTIND} " y Zsh pueden hacerlo con el parámetro de expansión de P parámetro , < código> ARGB = " $ {(P) OPTIND} " , pero se necesita el eval para ser POSIX completamente portátil. $ OPTIND es un número que se refiere al siguiente argumento de shell, por ejemplo, $ 2 (lo que significa que $ 1 es --bravo ) . El eval estaría interpretando ARG_B = " $ 2 " y debido a que esto se encuentra entre comillas, está a salvo del abuso (no pude encontrar una manera de engañar hacer algo inapropiado).

Garantizar un valor no vacío no es trivial y requiere verificarlo realmente, por lo que tenemos un condicional y generamos un error fatal en ese caso. Si permite que esté vacío, deberá condicionalizar el incremento de $ OPTIND o ejecutar un bucle infinito. [$ # -gt $ OPTIND] & amp; & amp; OPTIND = $ ((OPTIND + 1)) debería hacer.

La parte final de esta cláusula adicional incrementa $ OPTIND para absorber adecuadamente el argumento de la opción y pasar a la siguiente opción.

Eche un vistazo a shFlags que es una biblioteca de shell portátil (lo que significa : sh, bash, dash, ksh, zsh en Linux, Solaris, etc.).

Hace que agregar nuevos indicadores sea tan simple como agregar una línea a su script, y proporciona una función de uso generado automáticamente.

Aquí hay un simple ¡Hola, mundo! que usa 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}!"

Para los sistemas operativos que tienen el getopt mejorado que admite opciones largas (por ejemplo, Linux), puede hacer:

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

Por lo demás, debes usar la opción corta:

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

Agregar una nueva bandera es tan simple como agregar una nueva DEFINE_ call .

Uso de getopts con opciones cortas / largas y argumentos


Funciona con todas las combinaciones, por ejemplo:

  • foobar -f --bar
  • foobar --foo -b
  • foobar -bf --bar --foobar
  • foobar -fbFBAshorty --bar -FB --arguments = longhorn
  • foobar -fA " texto en corto " -B --arguments = " texto de cuernos largos "
  • bash foobar -F --barfoo
  • sh foobar -B --foobar - ...
  • bash ./foobar -F --bar

Algunas declaraciones para este ejemplo

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

Cómo se vería la función de uso

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 con banderas largas / cortas, así como argumentos largos

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

Salida

##################################################################
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"

Combinando lo anterior en un script cohesivo

#!/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

Otra forma ...

# 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

Resolví de esta manera:

# 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;

¿Estoy siendo tonto o algo así? getopt y getopts son muy confusos.

En caso de que no desee la dependencia getopt , puede hacer esto:

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

Por supuesto, entonces no puedes usar opciones de estilo largas con un guión. Y si desea agregar versiones abreviadas (por ejemplo, --verbos en lugar de --verbose), entonces debe agregarlas manualmente.

Pero si está buscando obtener la funcionalidad getopts junto con opciones largas, esta es una manera simple de hacerlo.

También puse este fragmento en un gist .

El getopts incorporado no puede hacer esto. Hay un programa externo getopt (1) que puede hacer esto, pero solo lo obtienes en Linux desde el paquete util-linux . Viene con un script de ejemplo getopt-parse.bash .

También hay un getopts_long escrito como una función de 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

En ksh93 , getopts no admite nombres largos ...

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

O eso dicen los tutoriales que he encontrado. Pruébalo y verás.

Inventar otra versión de la rueda ...

Esta función es (con suerte) un reemplazo de shell bourne simple compatible con POSIX para GNU getopt. Es compatible con opciones cortas / largas que pueden aceptar argumentos obligatorios / opcionales / no, y la forma en que se especifican las opciones es casi idéntica a la opción GNU getopt, por lo que la conversión es trivial.

Por supuesto, esto sigue siendo una parte considerable de código para colocar en un script, pero es aproximadamente la mitad de las líneas de la conocida función de shell getopt_long, y puede ser preferible en los casos en los que solo desee reemplazar los usos existentes de GNU getopt. .

Este es un código bastante nuevo, así que YMMV (y definitivamente, hágamelo saber si este no es realmente compatible con POSIX por alguna razón: la portabilidad fue la intención desde el principio, pero no tengo una prueba POSIX útil entorno).

Código y uso de ejemplo a continuación:

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

Ejemplo de uso:

# 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

Solo escribo scripts de shell de vez en cuando y quedo fuera de práctica, por lo que se agradece cualquier comentario.

Usando la estrategia propuesta por @Arvid Requate, notamos algunos errores de los usuarios. Un usuario que olvida incluir un valor accidentalmente tendrá el nombre de la siguiente opción tratado como un valor:

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

causará el valor de " loglevel " para ser visto como " - toc = VERDADERO " ;. Esto puede ser evitado.

He adaptado algunas ideas sobre la comprobación del error del usuario para CLI de http://mwiki.wooledge.org/BashFAQ / 035 discusión del análisis manual. Inserté el control de errores en el manejo de ambos " - " y " - " argumentos

Luego comencé a juguetear con la sintaxis, por lo que cualquier error aquí es estrictamente mi culpa, no de los autores originales.

Mi enfoque ayuda a los usuarios que prefieren ingresar durante mucho tiempo con o sin el signo igual. Es decir, debería tener la misma respuesta a " - loglevel 9 " como " - loglevel = 9 " ;. En el método - / space, no es posible saber con seguridad si el usuario olvida un argumento, por lo que es necesario adivinarlo.

  1. si el usuario tiene el formato de signo largo / igual (--opt =), entonces un espacio después de = desencadena un error porque no se proporcionó un argumento.
  2. si el usuario tiene argumentos largos / espaciales (--opt), este script causa un error si no sigue ningún argumento (final del comando) o si el argumento comienza con un guión)

En caso de que comiences con esto, hay una diferencia interesante entre " - opt = value " y " - valor opcional " formatos Con el signo igual, el argumento de la línea de comando se ve como " opt = valor " y el trabajo para manejar eso es el análisis de cadenas, para separar en el " = " ;. Por el contrario, con " - opt value " ;, el nombre del argumento es & opt; opt " y tenemos el desafío de obtener el siguiente valor suministrado en la línea de comandos. Ahí es donde @Arvid Requate usó $ {! OPTIND}, la referencia indirecta. Todavía no entiendo eso, bueno, en absoluto, y los comentarios en BashFAQ parecen advertir contra ese estilo ( http : //mywiki.wooledge.org/BashFAQ/006 ). Por cierto, no creo que los comentarios del póster anterior sobre la importancia de OPTIND = $ (($ OPTIND + 1)) sean correctos. Quiero decir, no veo ningún daño por omitirlo.

En la versión más reciente de este script, flag -v significa una impresión VERBOSE.

Guárdelo en un archivo llamado " cli-5.sh " ;, haga ejecutable, y cualquiera de estos funcionará o fallará de la manera deseada

./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

Aquí hay un ejemplo de salida de la comprobación de errores en intpu del usuario

$ ./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

Debería considerar activar -v, ya que imprime las partes internas de OPTIND y 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"

Aquí puede encontrar algunos enfoques diferentes para el análisis de opciones complejas en bash: http://mywiki.wooledge.org/ComplexOptionParsing

Creé el siguiente, y creo que es bueno, porque es un código mínimo y funcionan tanto las opciones largas como las cortas. Una opción larga también puede tener múltiples argumentos con este enfoque.

#!/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

He estado trabajando en ese tema durante bastante tiempo ... y he creado mi propia biblioteca, la cual necesitarás en tu script principal. Consulte libopt4shell y cd2mpc para ver un ejemplo. Espero que ayude!

Una solución mejorada:

# 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

Tal vez sea más simple usar ksh, solo para la parte de getopts, si necesita opciones de línea de comando largas, ya que puede ser más fácil hacerlo allí.

# 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

Todavía no tengo suficiente representante para comentar o votar su solución, pero la respuesta de sme funcionó extremadamente bien para yo. El único problema con el que me encontré fue que los argumentos terminan envueltos en comillas simples (así que tengo una tira de ellos).

También agregué algunos usos de ejemplo y texto de AYUDA. Incluiré mi versión ligeramente extendida aquí:

#!/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

Quería algo sin dependencias externas, con un estricto soporte de bash (-u), y lo necesitaba para funcionar incluso en las versiones anteriores de bash. Esto maneja varios tipos de parámetros:

  • bools cortos (-h)
  • opciones cortas (-i " imagen.jpg ")
  • long bools (--help)
  • es igual a las opciones (--archivo = " nombre_archivo.ext ")
  • opciones de espacio (--archivo " nombre de archivo.ext ")
  • bools concatinados (-hvm)

Simplemente inserte lo siguiente en la parte superior de su 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
}

Y úsalo así:

# 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 respuesta aceptada hace un muy buen trabajo al señalar todas las deficiencias de bash incorporado getopts . La respuesta termina con:

  

Por lo tanto, si bien es posible escribir más código para evitar la falta de soporte para opciones largas, esto es mucho más trabajo y anula parcialmente el propósito de usar un analizador getopt para simplificar su código.

Y aunque en principio estoy de acuerdo con esa afirmación, creo que la cantidad de veces que todos implementamos esta característica en varios scripts justifica poner un poco de esfuerzo en crear una solución estandarizada y bien probada.

Como tal, he actualizado " " bash construido en getopts implementando getopts_long en bash puro, sin dependencias externas. El uso de la función es 100% compatible con los getopts integrados.

Al incluir getopts_long (que está alojado en GitHub en https: // github. com / UmkaDK / getopts_long ) en un script, la respuesta a la pregunta original se puede implementar simplemente como:

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

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

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 

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

#!/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=

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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 )"

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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 "

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

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 

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

<*>

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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 )"

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde http://nt4.com/bash/argparser.inc.sh

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

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

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 

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

<*>

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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 )"

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

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 

Para mantener la compatibilidad entre plataformas y evitar la dependencia de ejecutables externos, porté algo de código de otro idioma.

Lo encuentro muy fácil de usar, aquí hay un ejemplo:

<*>

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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 )"

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde 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 )"

El BASH requerido es un poco más largo de lo que podría ser, pero quería evitar confiar en las matrices asociativas de BASH 4. También puede descargar esto directamente desde http://nt4.com/bash/argparser.inc.sh

<*>

Si todas sus opciones largas tienen primeros caracteres únicos y coincidentes como las opciones cortas, por ejemplo

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

Es lo mismo que

./slamm -c 23 -p test -q

Puede usar este antes getopts para reescribir $ 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
    .....

Gracias por mtvee por la inspiración ;-)

getopts incorporado solo analiza las opciones cortas (excepto en ksh93), pero aún puede agregar algunas líneas de secuencias de comandos para que getopts maneje opciones largas.

Aquí hay una parte del código que se encuentra en 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))

Aquí hay una prueba:

# 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

De lo contrario, en Korn Shell reciente ksh93, getopts puede analizar naturalmente las opciones largas e incluso mostrar una página de manual por igual. (Consulte http: // www .uxora.com / unix / shell-script / 20-getopts-with-man-page-and-long-options )

El getopt OS X (BSD) incorporado no admite opciones largas, pero la versión GNU sí lo hace: brew install gnu-getopt . Luego, algo similar a: cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt / usr / local / bin / gnu-getopt .

EasyOptions maneja opciones cortas y largas:

## 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 simple bricolaje para obtener solo nombres largos args:

Uso:

$ ./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 simplemente así es como quieres llamar al script

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

entonces puedes seguir esta forma más sencilla de lograrlo con la ayuda de getopt y --lopopciones

prueba esto, espero que esto sea útil

# 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top