Un esempio di come usare getopts in bash


345

Voglio chiamare il myscriptfile in questo modo:

$ ./myscript -s 45 -p any_string

o

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

I miei requisiti sono:

  • getopt qui per ottenere gli argomenti di input
  • verifica che -sesista, se non restituisce un errore
  • controlla che il valore dopo il -ssia 45 o 90
  • controlla che -pesista e che ci sia una stringa di input dopo
  • se l'utente inserisce ./myscript -ho semplicemente ./myscriptvisualizza la guida

Ho provato finora questo codice:

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

Ma con quel codice ottengo errori. Come farlo con Bash e getopt?


2
Le opzioni dovrebbero essere opzionali. Se si richiede il valore specificato da -s, ne fanno un argomento posizionale: ./myscript 45 anystring.
Chepner,

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

Va bene se in -prealtà è un'opzione (cioè, il tuo programma può procedere se non è presente). In questo caso ./myscript 45 -p any_string,. (Penso che sia in getoptgrado di gestire opzioni miste e argomenti posizionali, mentre il bashcomando integrato getoptsrichiede che tutti gli argomenti posizionali siano posizionati dopo le opzioni.)
chepner

Risposte:


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

Esempi di esecuzione:

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

19
Nella chiamata getopts, perché c'è un colon principale? Quando "h" ha i due punti dopo?
e40

7
Dovrebbe usage()davvero restituire 1?
Pithikos,

6
@Pithikos Un buon punto. Il buon senso mi dice che quando invocato tramite -hesso dovrebbe tornare 0, dopo aver colpito una bandiera inesistente dovrebbe tornare >0(per semplicità non ho fatto distinzione tra quei casi e nessuno ti costringe a stampare il testo di utilizzo in quest'ultimo caso) . Ho visto programmi che ritornano sempre != 0, comunque, anche su -h/--help. Forse dovrei aggiornare lo snippet nel caso in cui la gente lo usi come boilerplate (spero di no)?
Adrian Frühwirth,

1
@ A.Danischewski Questo è getoptsdesign ( '), non esistono "argomenti opzionali" con getopts. Il parser semplicemente non può sapere se il token successivo è un argomento per l'opzione corrente o un'opzione da solo poiché -ppotrebbe essere il valore desiderato. È possibile incidere intorno a questo se assolutamente sapere che un parametro di opzione non può apparire come un'altra opzione valida, sì, ma si potrebbe dire che c'è un motivo per argomenti opzionali non sono definiti in POSIX.
Adrian Frühwirth,

4
@ user1011471 Hai ragione! Le parentesi graffe, per così dire, aiutano il bashlessico nell'identificare le variabili. In molti casi non sono necessari e il fatto che io li usi sempre è solo una questione di stile di codifica personale. Per me, è più facile (e più bello) usarli sempre invece di ricordare le regole di analisi in merito all'ambiguità. Praticamente lo stesso motivo per cui si dovrebbe scrivere if (foo) { bar; }invece if (foo) bar;in un linguaggio in stile C (estetica e / o evitando errori sciocchi).
Adrian Frühwirth,

109

Il problema con il codice originale è che:

  • h:si aspetta un parametro dove non dovrebbe, quindi cambiarlo in solo h(senza due punti)
  • aspettarsi -p any_string, è necessario aggiungere p:all'elenco degli argomenti

Fondamentalmente :dopo l'opzione significa che richiede l'argomento.


La sintassi di base di getoptsè (vedi:) man bash:

getopts OPTSTRING VARNAME [ARGS...]

dove:

  • OPTSTRING è una stringa con un elenco di argomenti previsti,

    • h- controlla l'opzione -h senza parametri; dà errore su opzioni non supportate;
    • h:- verifica opzione -h con parametro; dà errori su opzioni non supportate;
    • abc- assegno per le opzioni -a, -b, -c; errori su opzioni non supportate;
    • :abc- assegno per le opzioni -a, -b, -c; elimina gli errori su opzioni non supportate;

      Note: in altre parole, i due punti davanti alle opzioni consentono di gestire gli errori nel codice. La variabile conterrà ?in caso di opzione non supportata, :in caso di valore mancante.

  • OPTARG - è impostato sul valore dell'argomento corrente,

  • OPTERR - indica se Bash deve visualizzare messaggi di errore.

Quindi il codice può essere:

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

Esempio di utilizzo:

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

Vedi: Tutorial per piccole getopts su Bash Hackers Wiki


2
Modificare la funzione utilizzo a questo: usage() { echo "$0 usage:" && grep "[[:space:]].)\ #" $0 | sed 's/#//' | sed -r 's/([a-z])\)/-\1/'; exit 0; }. Tiene conto solo di un singolo carattere spazio prima dell'opzione lettera, rimuove il # dal commento e antepone un '-' prima dell'opzione lettera rendendolo più chiaro per il comando.
poagester,

2
@kenorb: i due punti davanti alle opzioni non ignorano le opzioni non supportate ma silenziano gli errori da bash e ti permettono di gestirlo nel tuo codice. La variabile conterrà '?' nel caso di opzione non supportata e ':' in caso di valore mancante.
Hynek -Pichi- Vychodil,

1
Grazie per i documenti dettagliati, non sono stato in grado di ottenere il :diritto fino a quando non ho visto queste note. Dobbiamo aggiungere un :alle opzioni in cui ci aspettiamo un argomento.
Aukhan,

51

Uso getopt

Perché getopt?

Analizzare elaborati argomenti della riga di comando per evitare confusione e chiarire le opzioni che stiamo analizzando in modo che il lettore dei comandi possa capire cosa sta succedendo.

Che cos'è getopt?

getoptviene utilizzato per scomporre (analizzare) le opzioni nelle righe di comando per un facile analisi mediante procedure di shell e per verificare le opzioni legali. Per getopt(3)fare ciò usa le routine GNU .

getopt può avere i seguenti tipi di opzioni.

  1. Opzioni senza valore
  2. opzioni coppia chiave-valore

Nota: in questo documento, durante la spiegazione della sintassi:

  • Qualsiasi cosa all'interno di [] è un parametro facoltativo nella sintassi / esempi.
  • è un segnaposto, il che significa che dovrebbe essere sostituito con un valore effettivo.

COME USARLO getopt?

Sintassi: First Form

getopt optstring parameters

Esempi:

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

Qui h, v, t sono le opzioni e -h -v -t è come le opzioni dovrebbero essere fornite nella riga di comando.

  1. 'h' è un'opzione senza valore.
  2. 'v:' implica che l'opzione -v ha valore ed è un'opzione obbligatoria. ':' significa che ha un valore.
  3. 't ::' implica che l'opzione -t ha valore ma è facoltativa. '::' significa facoltativo.

Nel parametro opzionale, il valore non può avere una separazione di spazi bianchi con l'opzione. Quindi, nell'esempio "-t123", -t è l'opzione 123 è valore.

Sintassi: seconda forma

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

Qui dopo getopt è diviso in cinque parti

  • Il comando stesso vale a dire getopt
  • Il getopt_options, descrive come analizzare gli argomenti. opzioni a trattino singolo, opzioni a trattino doppio.
  • -, separa getopt_options dalle opzioni che si desidera analizzare e dalle opzioni brevi consentite
  • Le opzioni brevi, sono prese immediatamente dopo - è stato trovato. Proprio come la prima sintassi del modulo.
  • I parametri, queste sono le opzioni che hai passato nel programma. Le opzioni che si desidera analizzare e ottenere i valori effettivi impostati su di esse.

Esempi

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

Sintassi: terza forma

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

Qui dopo getopt è diviso in cinque parti

  • Il comando stesso vale a dire getopt
  • Il getopt_options, descrive come analizzare gli argomenti. opzioni a trattino singolo, opzioni a trattino doppio.
  • Le opzioni brevi, ad esempio -o o --opzioni. Proprio come la prima sintassi del modulo ma con l'opzione "-o" e prima del "-" (doppio trattino).
  • -, separa getopt_options dalle opzioni che si desidera analizzare e dalle opzioni brevi consentite
  • I parametri, queste sono le opzioni che hai passato nel programma. Le opzioni che si desidera analizzare e ottenere i valori effettivi impostati su di esse.

Esempi

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

GETOPT_OPTIONS

getopt_options cambia il modo in cui vengono analizzati i parametri della riga di comando.

Di seguito sono riportate alcune delle getopt_options

Opzione: -l o --longoptions

Significa che il comando getopt dovrebbe consentire il riconoscimento delle opzioni multi-carattere. Più opzioni sono separate da virgola.

Ad esempio, --name=Karthikè un'opzione lunga inviata nella riga di comando. In getopt, l'utilizzo di opzioni lunghe è simile

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

Poiché il nome: è specificato, l'opzione deve contenere un valore

Opzione: -a o --alternative

Significa che il comando getopt dovrebbe consentire all'opzione lunga di avere un singolo trattino '-' anziché un doppio trattino '-'.

Esempio, invece di --name=Karthikte potresti usare solo-name=Karthik

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

Un esempio di script completo con il codice:

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

Esecuzione di questo file di 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


getopt vs getopts .. conformità cross platform molto diversa
shadowbq

35

L'esempio fornito con getopt(la mia distribuzione l'ha inserito /usr/share/getopt/getopt-parse.bash) sembra coprire tutti i tuoi casi:

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

11
Il comando esterno getopt (1) non è mai sicuro da usare, a meno che tu non sappia che è GNU getopt, lo chiami in un modo specifico GNU e ti assicuri che GETOPT_COMPATIBLE non sia nell'ambiente. Utilizzare invece getopts (shell builtin) o semplicemente passare in rassegna i parametri posizionali.
Gilles Quenot,

@sputnick, tyvm, non lo sapevo.
Brian Cain,

14
Eh, nessun comando esterno è sicuro da usare da quello standard. In getopts integrate mancano funzionalità cruciali e se si desidera verificare la presenza di GETOPT_COMPATIBLE, è più semplice che eseguire il porting delle funzionalità di getopt.
Michael Terry,

12

So che è già stata data una risposta, ma per la cronaca e per chiunque abbia gli stessi requisiti ho deciso di pubblicare questa risposta correlata. Il codice è inondato di commenti per spiegare il codice.

Risposta aggiornata:

Salva il file come 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
}

Quindi puoi usarlo in questo modo:

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

Vecchia risposta:

Di recente ho dovuto usare un approccio generico. Mi sono imbattuto in questa soluzione:

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

Non l'ho provato approssimativamente, quindi potrei avere alcuni bug lì dentro.


1
Se si utilizza getoptsuna funzione, aggiungere local OPTIND OPTARGalla funzione
glenn jackman

@glennjackman in realtà è più un approccio sed piuttosto che usaregetopts
Sebastian

8

Esempio POSIX 7

Vale anche la pena controllare l'esempio dallo standard: 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" "$*"

E quindi possiamo provarlo:

$ 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

Testato in Ubuntu 17.10, shè trattino 0.5.8.


0

"getops" e "getopt" sono molto limitati. Sebbene "getopt" sia suggerito di non essere usato affatto, offre lunghe opzioni. Dove come "getopts" consente solo le opzioni a carattere singolo come "-a" "-b". Ci sono alcuni altri svantaggi quando si utilizza uno dei due.

Quindi ho scritto una piccola sceneggiatura che sostituisce "getopts" e "getopt". È un inizio, probabilmente potrebbe essere migliorato molto.

Aggiornamento 08-04-2020 : Ho aggiunto il supporto per i trattini, ad esempio "--package-name".

Uso: "./script.sh pacchetto install --package" nome con spazio "--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}"
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.