Un ejemplo de cómo usar getopts en bash

345

Quiero llamar al myscriptarchivo de esta manera:

$ ./myscript -s 45 -p any_string

o

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

Mis requisitos son:

  • getopt aquí para obtener los argumentos de entrada
  • compruebe que -sexiste, si no devuelve un error
  • compruebe que el valor después de -ses 45 o 90
  • compruebe que -pexiste y hay una cadena de entrada después
  • si el usuario ingresa ./myscript -ho simplemente ./myscriptmuestra ayuda

Intenté hasta ahora este código:

#!/bin/bash
while getopts "h:s:" arg; do
  case $arg in
    h)
      echo "usage" 
      ;;
    s)
      strength=$OPTARG
      echo $strength
      ;;
  esac
done

Pero con ese código obtengo errores. ¿Cómo hacerlo con Bash y getopt?

MOHAMED
fuente
2
Se supone que las opciones son opcionales. Si necesita el valor especificado por -s, lo convierten en un argumento posicional: ./myscript 45 anystring.
Chepner
@chepner$./myscript -s 45 -p any_string
MOHAMED
Está bien si en -prealidad es una opción (es decir, su programa puede continuar si no está presente). En este caso ./myscript 45 -p any_string,. (Creo que getoptpuede manejar opciones mixtas y argumentos posicionales, mientras que el bashcomando integrado getoptsrequiere que todos los argumentos posicionales para ser colocado después de las opciones.)
chepner

Respuestas:

513
#!/bin/bash

usage() { echo "Usage: $0 [-s <45|90>] [-p <string>]" 1>&2; exit 1; }

while getopts ":s:p:" o; do
    case "${o}" in
        s)
            s=${OPTARG}
            ((s == 45 || s == 90)) || usage
            ;;
        p)
            p=${OPTARG}
            ;;
        *)
            usage
            ;;
    esac
done
shift $((OPTIND-1))

if [ -z "${s}" ] || [ -z "${p}" ]; then
    usage
fi

echo "s = ${s}"
echo "p = ${p}"

Ejecuciones de ejemplo:

$ ./myscript.sh
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -h
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s "" -p ""
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 10 -p foo
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 45 -p foo
s = 45
p = foo

$ ./myscript.sh -s 90 -p bar
s = 90
p = bar
Adrian Frühwirth
fuente
19
En la llamada de getopts, ¿por qué hay un colon principal? ¿Cuándo "h" tiene dos puntos después?
e40
77
usage()Realmente debería devolver 1?
Pithikos
66
@Pithikos Buen punto. El sentido común me dice que cuando se invoca a través de -hél, debería regresar 0, al presionar un indicador no existente, debería regresar >0(por simplicidad, no diferenciaba entre esos casos y nadie lo obliga a imprimir el texto de uso en el último caso) . Sin != 0embargo, he visto programas que siempre regresan , incluso -h/--help. ¿Tal vez debería actualizar el fragmento en caso de que la gente use esto como repetitivo (espero que no)?
Adrian Frühwirth
1
@ A.Danischewski Esto es por ( getopts') diseño, no existe tal cosa como "argumentos opcionales" con getopts. El analizador simplemente no puede saber si el siguiente token es un argumento para la opción actual o una opción en sí misma, ya que -ppodría ser el valor deseado. Usted puede cortar alrededor de este si es absolutamente sabe que un parámetro de opción no puede verse como otra opción válida, sí, pero se podría decir que hay una razón argumentos opcionales no están definidos en POSIX.
Adrian Frühwirth
44
@ user1011471 ¡Estás en lo correcto! Las llaves, por así decirlo, solo ayudan al bashlexer a identificar variables. En muchos casos son innecesarios y el hecho de que siempre los use es solo una cuestión de estilo de codificación personal. Para mí, es más fácil (y más bonito) usarlos siempre en lugar de recordar las reglas de análisis con respecto a la ambigüedad. Más o menos lo mismo por qué uno escribiría en if (foo) { bar; }lugar de if (foo) bar;un lenguaje de estilo C (estética y / o evitar errores tontos).
Adrian Frühwirth
109

El problema con el código original es que:

  • h: espera el parámetro donde no debería, así que cámbielo a solo h (sin dos puntos)
  • para esperar -p any_string, debe agregar p:a la lista de argumentos

Básicamente :después de que la opción significa que requiere el argumento.


La sintaxis básica de getoptses (ver:) man bash:

getopts OPTSTRING VARNAME [ARGS...]

dónde:

  • OPTSTRING es una cadena con una lista de argumentos esperados,

    • h- Verifique la opción -h sin parámetros; da error en opciones no compatibles;
    • h:- verifique la opción -h con el parámetro; da errores en opciones no compatibles;
    • abc- verificación de opciones -a, -b, -c; da errores en opciones no compatibles;
    • :abc- verificación de opciones -a, -b, -c; silencia los errores en opciones no compatibles;

      Notas: En otras palabras, los dos puntos delante de las opciones le permiten manejar los errores en su código. La variable contendrá ?en el caso de una opción no admitida, :en el caso de un valor perdido.

  • OPTARG - se establece en el valor del argumento actual,

  • OPTERR - indica si Bash debería mostrar mensajes de error.

Entonces el código puede ser:

#!/usr/bin/env bash
usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; }
[ $# -eq 0 ] && usage
while getopts ":hs:p:" arg; do
  case $arg in
    p) # Specify p value.
      echo "p is ${OPTARG}"
      ;;
    s) # Specify strength, either 45 or 90.
      strength=${OPTARG}
      [ $strength -eq 45 -o $strength -eq 90 ] \
        && echo "Strength is $strength." \
        || echo "Strength needs to be either 45 or 90, $strength found instead."
      ;;
    h | *) # Display help.
      usage
      exit 0
      ;;
  esac
done

Ejemplo de uso:

$ ./foo.sh 
./foo.sh usage:
    p) # Specify p value.
    s) # Specify strength, either 45 or 90.
    h | *) # Display help.
$ ./foo.sh -s 123 -p any_string
Strength needs to be either 45 or 90, 123 found instead.
p is any_string
$ ./foo.sh -s 90 -p any_string
Strength is 90.
p is any_string

Ver: Pequeño tutorial de getopts en Bash Hackers Wiki

kenorb
fuente
2
Cambiar la función de uso a esto: usage() { echo "$0 usage:" && grep "[[:space:]].)\ #" $0 | sed 's/#//' | sed -r 's/([a-z])\)/-\1/'; exit 0; }. Solo representa un solo carácter de espacio en blanco antes de la opción de letra, elimina el # del comentario y antepone un '-' antes de la opción de letra, lo que lo hace más claro para el comando.
poagester
2
@kenorb: Colon delante de las opciones no ignora las opciones no compatibles, pero silencia los errores de bash y le permite manejarlo en su código. La variable contendrá '?' en el caso de la opción no admitida y ':' en el caso de falta de valor.
Hynek -Pichi- Vychodil
1
Gracias por los documentos detallados, no pude obtener el :correcto hasta que vi estas notas. Necesitamos agregar un :a las opciones donde esperamos un argumento.
Aukhan
51

Utilizar getopt

¿Por qué getopt?

Analizar argumentos elaborados de la línea de comandos para evitar confusiones y aclarar las opciones que estamos analizando para que el lector de los comandos pueda entender lo que está sucediendo.

¿Qué es getopt?

getoptse utiliza para dividir (analizar) las opciones en las líneas de comando para facilitar el análisis mediante procedimientos de shell y para buscar opciones legales. Utiliza las getopt(3)rutinas GNU para hacer esto.

getopt puede tener los siguientes tipos de opciones.

  1. Opciones sin valor
  2. opciones de par clave-valor

Nota: En este documento, durante la explicación de la sintaxis:

  • Cualquier cosa dentro de [] es un parámetro opcional en la sintaxis / ejemplos.
  • es un marcador de posición, lo que significa que debe sustituirse por un valor real.

¿CÓMO UTILIZAR getopt?

Sintaxis: primera forma

getopt optstring parameters

Ejemplos:

# This is correct
getopt "hv:t::" "-v 123 -t123"  
getopt "hv:t::" "-v123 -t123"  # -v and 123 doesn't have whitespace

# -h takes no value.
getopt "hv:t::" "-h -v123"


# This is wrong. after -t can't have whitespace.
# Only optional params cannot have whitespace between key and value
getopt "hv:t::" "-v 123 -t 123"

# Multiple arguments that takes value.
getopt "h:v:t::g::" "-h abc -v 123 -t21"

# Multiple arguments without value
# All of these are correct
getopt "hvt" "-htv"
getopt "hvt" "-h -t -v"
getopt "hvt" "-tv -h"

Aquí h, v, t son las opciones y -h -v -t es cómo se deben dar las opciones en la línea de comandos.

  1. 'h' es una opción sin valor.
  2. 'v:' implica que la opción -v tiene valor y es una opción obligatoria. ':' significa que tiene un valor.
  3. 't ::' implica que la opción -t tiene valor pero es opcional. '::' significa opcional.

En el parámetro opcional, el valor no puede tener separación de espacios en blanco con la opción. Entonces, en el ejemplo "-t123", -t es la opción 123 es el valor.

Sintaxis: Segunda forma

getopt [getopt_options] [--] [optstring] [parameters]

Aquí después de getopt se divide en cinco partes

  • El comando en sí, es decir, getopt
  • Las getopt_options, describe cómo analizar los argumentos. opciones largas de un solo guion, opciones de doble guion.
  • -, separa las opciones getopt de las opciones que desea analizar y las opciones cortas permitidas
  • Las opciones cortas, se toman inmediatamente después de que se encuentra. Al igual que la primera sintaxis del formulario.
  • Los parámetros, estas son las opciones que ha pasado al programa. Las opciones que desea analizar y obtener los valores reales establecidos en ellas.

Ejemplos

getopt -l "name:,version::,verbose" -- "n:v::V" "--name=Karthik -version=5.2 -verbose"

Sintaxis: Tercera forma

getopt [getopt_options] [-o options] [--] [optstring] [parameters]

Aquí después de getopt se divide en cinco partes

  • El comando en sí, es decir, getopt
  • Las getopt_options, describe cómo analizar los argumentos. opciones largas de un solo guion, opciones de doble guion.
  • Las opciones cortas es decir -o u --opciones. Al igual que la primera sintaxis del formulario, pero con la opción "-o" y antes del "-" (guión doble).
  • -, separa las opciones getopt de las opciones que desea analizar y las opciones cortas permitidas
  • Los parámetros, estas son las opciones que ha pasado al programa. Las opciones que desea analizar y obtener los valores reales establecidos en ellas.

Ejemplos

getopt -l "name:,version::,verbose" -a -o "n:v::V" -- "-name=Karthik -version=5.2 -verbose"

GETOPT_OPTIONS

getopt_options cambia la forma en que se analizan los parámetros de la línea de comandos.

A continuación se muestran algunas de las opciones getopt

Opción: -l o --longoptions

El comando getopt de medios debe permitir que se reconozcan las opciones de varios caracteres. Las opciones múltiples están separadas por comas.

Por ejemplo, --name=Karthikes una opción larga enviada en línea de comando. En getopt, el uso de opciones largas es como

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

Como nombre: se especifica, la opción debe contener un valor

Opción: -a o --alternativa

El comando getopt significa que la opción larga debe tener un solo guión '-' en lugar de un guión doble '-'.

Ejemplo, en lugar de --name=Karthikusted podría usar solo-name=Karthik

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

Un ejemplo completo de script con el código:

#!/bin/bash

# filename: commandLine.sh
# author: @theBuzzyCoder

showHelp() {
# `cat << EOF` This means that cat should stop reading when EOF is detected
cat << EOF  
Usage: ./installer -v <espo-version> [-hrV]
Install Pre-requisites for EspoCRM with docker in Development mode

-h, -help,          --help                  Display help

-v, -espo-version,  --espo-version          Set and Download specific version of EspoCRM

-r, -rebuild,       --rebuild               Rebuild php vendor directory using composer and compiled css using grunt

-V, -verbose,       --verbose               Run script in verbose mode. Will print out each step of execution.

EOF
# EOF is found above and hence cat command stops reading. This is equivalent to echo but much neater when printing out.
}


export version=0
export verbose=0
export rebuilt=0

# $@ is all command line parameters passed to the script.
# -o is for short options like -v
# -l is for long options with double dash like --version
# the comma separates different long options
# -a is for long options with single dash like -version
options=$(getopt -l "help,version:,verbose,rebuild,dryrun" -o "hv:Vrd" -a -- "$@")

# set --:
# If no arguments follow this option, then the positional parameters are unset. Otherwise, the positional parameters 
# are set to the arguments, even if some of them begin with a ‘-’.
eval set -- "$options"

while true
do
case $1 in
-h|--help) 
    showHelp
    exit 0
    ;;
-v|--version) 
    shift
    export version=$1
    ;;
-V|--verbose)
    export verbose=1
    set -xv  # Set xtrace and verbose mode.
    ;;
-r|--rebuild)
    export rebuild=1
    ;;
--)
    shift
    break;;
esac
shift
done

Ejecutando este archivo de script:

# With short options grouped together and long option
# With double dash '--version'

bash commandLine.sh --version=1.0 -rV
# With short options grouped together and long option
# With single dash '-version'

bash commandLine.sh -version=1.0 -rV

# OR with short option that takes value, value separated by whitespace
# by key

bash commandLine.sh -v 1.0 -rV

# OR with short option that takes value, value without whitespace
# separation from key.

bash commandLine.sh -v1.0 -rV

# OR Separating individual short options

bash commandLine.sh -v1.0 -r -V
theBuzzyCoder
fuente
fuente: linkedin.com/pulse/…
theBuzzyCoder
getopt vs getopts ... muy diferente cumplimiento multiplataforma
shadowbq
35

El ejemplo empaquetado con getopt(mi distribución lo puso /usr/share/getopt/getopt-parse.bash) parece que cubre todos sus casos:

#!/bin/bash

# A small example program for using the new getopt(1) program.
# This program will only work with bash(1)
# An similar program using the tcsh(1) script language can be found
# as parse.tcsh

# Example input and output (from the bash prompt):
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
# Option a
# Option c, no argument
# Option c, argument `more'
# Option b, argument ` very long '
# Remaining arguments:
# --> `par1'
# --> `another arg'
# --> `wow!*\?'

# Note that we use `"$@"' to let each command-line parameter expand to a 
# separate word. The quotes around `$@' are essential!
# We need TEMP as the `eval set --' would nuke the return value of getopt.
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n 'example.bash' -- "$@"`

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

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

while true ; do
    case "$1" in
        -a|--a-long) echo "Option a" ; shift ;;
        -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
        -c|--c-long) 
            # c has an optional argument. As we are in quoted mode,
            # an empty parameter will be generated if its optional
            # argument is not found.
            case "$2" in
                "") echo "Option c, no argument"; shift 2 ;;
                *)  echo "Option c, argument \`$2'" ; shift 2 ;;
            esac ;;
        --) shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done
echo "Remaining arguments:"
for arg do echo '--> '"\`$arg'" ; done
Brian Cain
fuente
11
El comando externo getopt (1) nunca es seguro de usar, a menos que sepa que es GNU getopt, lo llama de una manera específica de GNU y se asegura de que GETOPT_COMPATIBLE no esté en el entorno. Utilice getopts (shell incorporado) en su lugar, o simplemente repita los parámetros posicionales.
Gilles Quenot
@sputnick, tyvm, no sabía esto.
Brian Cain
14
Eh, ningún comando externo es seguro de usar por ese estándar. Getopts incorporado carece de características cruciales y si desea verificar GETOPT_COMPATIBLE, eso es más fácil que portar las características de getopt.
Michael Terry
12

Sé que esto ya está respondido, pero para el registro y para cualquier persona con los mismos requisitos que yo, decidí publicar esta respuesta relacionada. El código está inundado de comentarios para explicar el código.

Respuesta actualizada:

Guarde el archivo como getopt.sh:

#!/bin/bash

function get_variable_name_for_option {
    local OPT_DESC=${1}
    local OPTION=${2}
    local VAR=$(echo ${OPT_DESC} | sed -e "s/.*\[\?-${OPTION} \([A-Z_]\+\).*/\1/g" -e "s/.*\[\?-\(${OPTION}\).*/\1FLAG/g")

    if [[ "${VAR}" == "${1}" ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

function parse_options {
    local OPT_DESC=${1}
    local INPUT=$(get_input_for_getopts "${OPT_DESC}")

    shift
    while getopts ${INPUT} OPTION ${@};
    do
        [ ${OPTION} == "?" ] && usage
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
            [ "${VARNAME}" != "" ] && eval "${VARNAME}=${OPTARG:-true}" # && printf "\t%s\n" "* Declaring ${VARNAME}=${!VARNAME} -- OPTIONS='$OPTION'"
    done

    check_for_required "${OPT_DESC}"

}

function check_for_required {
    local OPT_DESC=${1}
    local REQUIRED=$(get_required "${OPT_DESC}" | sed -e "s/\://g")
    while test -n "${REQUIRED}"; do
        OPTION=${REQUIRED:0:1}
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
                [ -z "${!VARNAME}" ] && printf "ERROR: %s\n" "Option -${OPTION} must been set." && usage
        REQUIRED=${REQUIRED:1}
    done
}

function get_input_for_getopts {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_optional {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/[^[]*\(\[[^]]*\]\)[^[]*/\1/g" -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_required {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/\[[^[]*\]//g" -e "s/[][ -]//g"
}

function usage {
    printf "Usage:\n\t%s\n" "${0} ${OPT_DESC}"
    exit 10
}

Entonces puedes usarlo así:

#!/bin/bash
#
# [ and ] defines optional arguments
#

# location to getopts.sh file
source ./getopt.sh
USAGE="-u USER -d DATABASE -p PASS -s SID [ -a START_DATE_TIME ]"
parse_options "${USAGE}" ${@}

echo ${USER}
echo ${START_DATE_TIME}

Vieja respuesta:

Hace poco necesitaba usar un enfoque genérico. Encontré esta solución:

#!/bin/bash
# Option Description:
# -------------------
#
# Option description is based on getopts bash builtin. The description adds a variable name feature to be used
# on future checks for required or optional values.
# The option description adds "=>VARIABLE_NAME" string. Variable name should be UPPERCASE. Valid characters
# are [A-Z_]*.
#
# A option description example:
#   OPT_DESC="a:=>A_VARIABLE|b:=>B_VARIABLE|c=>C_VARIABLE"
#
# -a option will require a value (the colon means that) and should be saved in variable A_VARIABLE.
# "|" is used to separate options description.
# -b option rule applies the same as -a.
# -c option doesn't require a value (the colon absense means that) and its existence should be set in C_VARIABLE
#
#   ~$ echo get_options ${OPT_DESC}
#   a:b:c
#   ~$
#


# Required options 
REQUIRED_DESC="a:=>REQ_A_VAR_VALUE|B:=>REQ_B_VAR_VALUE|c=>REQ_C_VAR_FLAG"

# Optional options (duh)
OPTIONAL_DESC="P:=>OPT_P_VAR_VALUE|r=>OPT_R_VAR_FLAG"

function usage {
    IFS="|"
    printf "%s" ${0}
    for i in ${REQUIRED_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
    printf " %s" "-${i:0:1} $VARNAME"
    done

    for i in ${OPTIONAL_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
        printf " %s" "[-${i:0:1} $VARNAME]"
    done
    printf "\n"
    unset IFS
    exit
}

# Auxiliary function that returns options characters to be passed
# into 'getopts' from a option description.
# Arguments:
#   $1: The options description (SEE TOP)
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   OPTIONS=$(get_options ${OPT_DESC})
#   echo "${OPTIONS}"
#
# Output:
#   "h:f:PW"
function get_options {
    echo ${1} | sed -e "s/\([a-zA-Z]\:\?\)=>[A-Z_]*|\?/\1/g"
}

# Auxiliary function that returns all variable names separated by '|'
# Arguments:
#       $1: The options description (SEE TOP)
#
# Example:
#       OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#       VARNAMES=$(get_values ${OPT_DESC})
#       echo "${VARNAMES}"
#
# Output:
#       "H_VAR|F_VAR|P_VAR|W_VAR"
function get_variables {
    echo ${1} | sed -e "s/[a-zA-Z]\:\?=>\([^|]*\)/\1/g"
}

# Auxiliary function that returns the variable name based on the
# option passed by.
# Arguments:
#   $1: The options description (SEE TOP)
#   $2: The option which the variable name wants to be retrieved
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   H_VAR=$(get_variable_name ${OPT_DESC} "h")
#   echo "${H_VAR}"
#
# Output:
#   "H_VAR"
function get_variable_name {
    VAR=$(echo ${1} | sed -e "s/.*${2}\:\?=>\([^|]*\).*/\1/g")
    if [[ ${VAR} == ${1} ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

# Gets the required options from the required description
REQUIRED=$(get_options ${REQUIRED_DESC})

# Gets the optional options (duh) from the optional description
OPTIONAL=$(get_options ${OPTIONAL_DESC})

# or... $(get_options "${OPTIONAL_DESC}|${REQUIRED_DESC}")

# The colon at starts instructs getopts to remain silent
while getopts ":${REQUIRED}${OPTIONAL}" OPTION
do
    [[ ${OPTION} == ":" ]] && usage
    VAR=$(get_variable_name "${REQUIRED_DESC}|${OPTIONAL_DESC}" ${OPTION})
    [[ -n ${VAR} ]] && eval "$VAR=${OPTARG}"
done

shift $(($OPTIND - 1))

# Checks for required options. Report an error and exits if
# required options are missing.

# Using function version ...
VARS=$(get_variables ${REQUIRED_DESC})
IFS="|"
for VARNAME in $VARS;
do
    [[ -v ${VARNAME} ]] || usage
done
unset IFS

# ... or using IFS Version (no function)
OLDIFS=${IFS}
IFS="|"
for i in ${REQUIRED_DESC};
do
    VARNAME=$(echo $i | sed -e "s/.*=>//g")
    [[ -v ${VARNAME} ]] || usage
    printf "%s %s %s\n" "-${i:0:1}" "${!VARNAME:=present}" "${VARNAME}"
done
IFS=${OLDIFS}

No probé esto más o menos, así que podría tener algunos errores allí.

Sebastian
fuente
1
Si está utilizando getoptsuna función, agregue local OPTIND OPTARGa la función
glenn jackman
@glennjackman en realidad es más como un enfoque sed en lugar de usargetopts
Sebastian
8

POSIX 7 ejemplo

También vale la pena consultar el ejemplo del estándar: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)   printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"

Y luego podemos probarlo:

$ sh a.sh
Remaining arguments are: 
$ sh a.sh -a
Option -a specified
Remaining arguments are: 
$ sh a.sh -b
No arg for -b option
Usage: a.sh: [-a] [-b value] args
$ sh a.sh -b myval
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh -a -b myval
Option -a specified
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh remain
Remaining arguments are: remain
$ sh a.sh -- -a remain
Remaining arguments are: -a remain

Probado en Ubuntu 17.10, shes el guión 0.5.8.

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

"getops" y "getopt" son muy limitados. Si bien se sugiere que "getopt" no se use en absoluto, ofrece opciones largas. Donde como "getopts" solo permite opciones de caracteres individuales como "-a" "-b". Hay algunas desventajas más al usar cualquiera de los dos.

Así que escribí un pequeño script que reemplaza "getopts" y "getopt". Es un comienzo, probablemente podría mejorarse mucho.

Actualización 08-04-2020 : he agregado soporte para guiones, por ejemplo, "--package-name".

Uso: "./script.sh package install --package" name with space "--build --archive"

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

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

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