risposta-alla-domanda-sullo-sviluppo-web-bd.com

Come analizzo gli argomenti della riga di comando in Bash?

Di ', ho uno script che viene chiamato con questa linea:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

o questo:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

Qual è il modo accettato di analizzare ciò in modo che in ogni caso (o una combinazione dei due) $v, $f e $d saranno tutti impostati su true e $outFile sarà uguale a /fizz/someOtherFile?

1521

Metodo 1: usare bash senza getopt [s]

Due modi comuni per passare argomenti key-value-pair sono:

Bash Space-Separated (ad esempio, --option argument) (senza getopt [s])

Uso ./myscript.sh -e conf -s /etc -l /usr/lib /etc/hosts

#!/bin/bash

POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -e|--extension)
    EXTENSION="$2"
    shift # past argument
    shift # past value
    ;;
    -s|--searchpath)
    SEARCHPATH="$2"
    shift # past argument
    shift # past value
    ;;
    -l|--lib)
    LIBPATH="$2"
    shift # past argument
    shift # past value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument
    ;;
    *)    # unknown option
    POSITIONAL+=("$1") # save it in an array for later
    shift # past argument
    ;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

echo FILE EXTENSION  = "${EXTENSION}"
echo SEARCH PATH     = "${SEARCHPATH}"
echo LIBRARY PATH    = "${LIBPATH}"
echo DEFAULT         = "${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi

Bash Equals-Separated (ad esempio, --option=argument) (senza getopt [s])

Uso ./myscript.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

#!/bin/bash

for i in "[email protected]"
do
case $i in
    -e=*|--extension=*)
    EXTENSION="${i#*=}"
    shift # past argument=value
    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    shift # past argument=value
    ;;
    -l=*|--lib=*)
    LIBPATH="${i#*=}"
    shift # past argument=value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument with no value
    ;;
    *)
          # unknown option
    ;;
esac
done
echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1
fi

Per capire meglio ${i#*=} cerca "Rimozione sottostringa" in questa guida . È funzionalmente equivalente a `sed 's/[^=]*=//' <<< "$i"` che chiama un sottoprocesso inutile o `echo "$i" | sed 's/[^=]*=//'` che chiama due sottoprocessi inutili. 

Metodo n. 2: uso di bash con getopt [s]

da: http://mywiki.wooledge.org/BashFAQ/035#getopts

getopt (1) limitations (versioni getopt più vecchie, relativamente recenti): 

  • non può gestire argomenti che sono stringhe vuote
  • non può gestire argomenti con spazi bianchi incorporati

Le versioni getopt più recenti non hanno queste limitazioni.

Inoltre, la shell POSIX (e altre) offre getopts che non ha queste limitazioni. Ecco un esempio getopts semplicistico:

#!/bin/sh

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the Shell.

# Initialize our own variables:
output_file=""
verbose=0

while getopts "h?vf:" opt; do
    case "$opt" in
    h|\?)
        show_help
        exit 0
        ;;
    v)  verbose=1
        ;;
    f)  output_file=$OPTARG
        ;;
    esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift

echo "verbose=$verbose, output_file='$output_file', Leftovers: [email protected]"

# End of file

I vantaggi di getopts sono:

  1. È più portatile e funzionerà con altre shell come dash
  2. Può gestire automaticamente più opzioni singole come -vf filename nel tipico modo Unix.

Lo svantaggio di getopts è che può gestire solo le opzioni brevi (-h, non --help) senza codice aggiuntivo.

Esiste un tutorial su getopts che spiega cosa significano tutte le sintassi e le variabili. In bash, c'è anche help getopts, che potrebbe essere informativo.

2173
Bruno Bronosky

Nessuna risposta cita enhanced getopt. E la risposta più votata è fuorviante: Ignora le opzioni brevi di stile -⁠vfd (richieste dall'OP), le opzioni dopo gli argomenti posizionali (richieste anche dall'OP) e ignora gli errori di parsing. Anziché:

  • Usa getopt avanzato da util-linux o precedentemente GNU glibc.1
  • Funziona con getopt_long() la funzione C di GNU glibc.
  • Ha all caratteristiche distintive utili (gli altri non ce l'hanno):
    • gestisce gli spazi, citando i caratteri e persino gli argomenti binari2
    • può gestire le opzioni alla fine: script.sh -o outFile file1 file2 -v
    • consente opzioni =-style: script.sh --outfile=fileOut --infile fileIn
  • È così vecchio già3 questo sistema GNU manca questo (ad esempio, qualsiasi Linux lo ha).
  • Puoi testare la sua esistenza con: getopt --test → return value 4.
  • Altro getopt o getopts incorporato nella shell sono di uso limitato.

Le seguenti chiamate

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

tutti ritornano

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

con il seguente myscript

#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset

! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo 'I’m sorry, `getopt --test` failed in this environment.'
    exit 1
fi

OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose

# -use ! and PIPESTATUS to get exit code with errexit set
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via   -- "[email protected]"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "[email protected]")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

1 getopt avanzato è disponibile sulla maggior parte dei "sistemi bash", incluso Cygwin; su OS X prova brew install gnu-getopt o Sudo port install getopt
2 le convenzioni POSIX exec() non hanno un modo affidabile per passare NULL binario negli argomenti della riga di comando; quei byte terminano prematuramente l'argomento
3 prima versione rilasciata nel 1997 o prima (l'ho rintracciata solo nel 1997)

419
Robert Siemer

da: digitalpeer.com con modifiche minori

Uso myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "[email protected]"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

Per capire meglio ${i#*=} cerca "Rimozione sottostringa" in questa guida . È funzionalmente equivalente a `sed 's/[^=]*=//' <<< "$i"` che chiama un sottoprocesso inutile o `echo "$i" | sed 's/[^=]*=//'` che chiama two sottoprocessi inutili.

114
guneysus

getopt()/getopts() è una buona opzione. Rubato da qui :

Il semplice uso di "getopt" è mostrato in questo mini-script:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

Quello che abbiamo detto è che nessuno di -a, -b, -c o -d sarà permesso, ma che -c è seguito da un argomento (il "c:" dice quello).

Se chiamiamo questa "g" e proviamo:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

Iniziamo con due argomenti e "getopt" rompe le opzioni e mette ciascuno nel proprio argomento. Anche aggiunto "-".

102
Matt J

A rischio di aggiungere un altro esempio da ignorare, ecco il mio schema.

  • gestisce -n arg e --name=arg
  • consente argomenti alla fine
  • mostra errori sensati se qualcosa è errato
  • compatibile, non usa i bashismi
  • leggibile, non richiede il mantenimento dello stato in un ciclo

Spero sia utile a qualcuno.

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;

    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done
84
bronson

Modo più succinto

script.sh

#!/bin/bash

while [[ "$#" -gt 0 ]]; do case $1 in
  -d|--deploy) deploy="$2"; shift;;
  -u|--uglify) uglify=1;;
  *) echo "Unknown parameter passed: $1"; exit 1;;
esac; shift; done

echo "Should deploy? $deploy"
echo "Should uglify? $uglify"

Utilizzo:

./script.sh -d dev -u

# OR:

./script.sh --deploy dev --uglify
78
Inanc Gumus

Sono in ritardo di circa 4 anni per questa domanda, ma voglio dare indietro. Ho usato le risposte precedenti come punto di partenza per riordinare la mia vecchia param parazione ad hoc. Ho quindi rifatto il seguente codice di modello. Gestisce sia i parametri lunghi che quelli brevi, usando argomenti = o separati dallo spazio, oltre a più parametri brevi raggruppati insieme. Infine inserisce nuovamente qualsiasi argomento non param nelle variabili $ 1, $ 2 ... Spero sia utile.

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 [email protected] ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done


# Code template for parsing command line parameters using only portable Shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the Shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS


echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done
39
Shane Day

La mia risposta si basa in gran parte su la risposta di Bruno Bronosky , ma ho praticamente schiacciato le sue due purissime implementazioni bash in una che uso molto spesso.

# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
    key="$1"
    case "$key" in
        # This is a flag type option. Will catch either -f or --foo
        -f|--foo)
        FOO=1
        ;;
        # Also a flag type option. Will catch either -b or --bar
        -b|--bar)
        BAR=1
        ;;
        # This is an arg value type option. Will catch -o value or --output-file value
        -o|--output-file)
        shift # past the key and to the value
        OUTPUTFILE="$1"
        ;;
        # This is an arg=value type option. Will catch -o=value or --output-file=value
        -o=*|--output-file=*)
        # No need to shift here since the value is part of the same string
        OUTPUTFILE="${key#*=}"
        ;;
        *)
        # Do whatever you want with extra options
        echo "Unknown option '$key'"
        ;;
    esac
    # Shift after checking all the cases to get the next option
    shift
done

Ciò consente di avere sia opzioni/valori separati dallo spazio, sia valori uguali definiti.

Quindi puoi eseguire il tuo script usando:

./myscript --foo -b -o /fizz/file.txt

così come:

./myscript -f --bar -o=/fizz/file.txt

ed entrambi dovrebbero avere lo stesso risultato finale.

PROFESSIONISTI:

  • Consente sia -arg = valore e -arg valore

  • Funziona con qualsiasi nome arg che puoi usare in bash

    • Significato -a o -arg o --arg o -a-r-g o altro
  • Pure bash. Non c'è bisogno di imparare/usare getopt o getopts

CONS:

  • Impossibile combinare argomenti

    • Significato no -abc. Devi fare -a -b -c

Questi sono gli unici pro/contro che posso pensare in cima alla mia testa

25
Ponyboy47

Ho trovato la questione di scrivere l'analisi portatile negli script in modo così frustrante che ho scritto Argbash - un generatore di codice FOSS in grado di generare il codice di analisi degli argomenti per il tuo script più ha alcune caratteristiche di Nizza:

https://argbash.io

24
bubla

Espandendo sulla risposta eccellente di @guneysus, ecco un tweak che consente all'utente di utilizzare qualsiasi sintassi che preferiscono, ad es.

command -x=myfilename.ext --another_switch 

vs

command -x myfilename.ext --another_switch

Vale a dire che gli uguali possono essere sostituiti con spazi bianchi. 

Questa "interpretazione fuzzy" potrebbe non essere di vostro gradimento, ma se state realizzando script che sono intercambiabili con altre utilità (come nel mio caso, che deve funzionare con ffmpeg), la flessibilità è utile.

STD_IN=0

prefix=""
key=""
value=""
for keyValue in "[email protected]"
do
  case "${prefix}${keyValue}" in
    -i=*|--input_filename=*)  key="-i";     value="${keyValue#*=}";; 
    -ss=*|--seek_from=*)      key="-ss";    value="${keyValue#*=}";;
    -t=*|--play_seconds=*)    key="-t";     value="${keyValue#*=}";;
    -|--stdin)                key="-";      value=1;;
    *)                                      value=$keyValue;;
  esac
  case $key in
    -i) MOVIE=$(resolveMovie "${value}");  prefix=""; key="";;
    -ss) SEEK_FROM="${value}";          prefix=""; key="";;
    -t)  PLAY_SECONDS="${value}";           prefix=""; key="";;
    -)   STD_IN=${value};                   prefix=""; key="";; 
    *)   prefix="${keyValue}=";;
  esac
done
13
unsynchronized

Penso che questo sia abbastanza semplice da usare:

#!/bin/bash
#

readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'

opts=vfdo:

# Enumerating options
while eval $readopt
do
    echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done

# Enumerating arguments
for arg
do
    echo ARG:$arg
done

Esempio di invito:

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile
13
Alek

Ti do la funzione parse_params che analizzerà i parametri dalla riga di comando.

  1. È una pura soluzione Bash, senza utilità aggiuntive.
  2. Non inquina l'ambito globale. 
  3. Restituisce senza sforzo semplici variabili da utilizzare, su cui è possibile creare ulteriore logica.
  4. La quantità di trattini prima dei parametri non ha importanza (--all equivale a -all equivale a all=all)

Lo script sotto è una dimostrazione di lavoro copia-incolla. Vedi la funzione show_use per capire come usare parse_params.

Limitazioni: 

  1. Non supporta parametri delimitati da spazi (-d 1)
  2. I nomi dei parametri perderanno i trattini, quindi --any-param e -anyparam sono equivalenti
  3. eval $(parse_params "[email protected]") deve essere usato all'interno di bash function (non funzionerà nello scope globale)

#!/bin/bash

# Universal Bash parameter parsing
# Parses equal sign separated params into local variables (--name=bob creates variable $name=="bob")
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Parses un-named params into ${ARGV[*]} array
# Additionally puts all named params raw into ${ARGN[*]} array
# Additionally puts all standalone "option" params raw into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4 (Jun-26-2018)
parse_params ()
{
    local existing_named
    local ARGV=() # un-named params
    local ARGN=() # named params
    local ARGO=() # options (--params)
    echo "local ARGV=(); local ARGN=(); local ARGO=();"
    while [[ "$1" != "" ]]; do
        # Escape asterisk to prevent bash asterisk expansion
        _escaped=${1/\*/\'\"*\"\'}
        # If equals delimited named parameter
        if [[ "$1" =~ ^..*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # val is everything after key and = (protect from param==value error)
            local _val="${1/$_key=}"
            # remove dashes from key name
            _key=${_key//\-}
            # skip when key is empty
            if [[ "$_key" == "" ]]; then
                shift
                continue
            fi
            # search for existing parameter name
            if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
                # if name already exists then it's a multi-value named parameter
                # re-declare it as an array if needed
                if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
                    echo "$_key=(\"\$$_key\");"
                fi
                # append new value
                echo "$_key+=('$_val');"
            else
                # single-value named parameter
                echo "local $_key=\"$_val\";"
                existing_named=" $_key"
            fi
        # If standalone named parameter
        Elif [[ "$1" =~ ^\-. ]]; then
            # remove dashes
            local _key=${1//\-}
            # skip when key is empty
            if [[ "$_key" == "" ]]; then
                shift
                continue
            fi
            # Add to options array
            echo "ARGO+=('$_escaped');"
            echo "local $_key=\"$_key\";"
        # non-named parameter
        else
            # Escape asterisk to prevent bash asterisk expansion
            _escaped=${1/\*/\'\"*\"\'}
            echo "ARGV+=('$_escaped');"
        fi
        shift
    done
}

#--------------------------- DEMO OF THE USAGE -------------------------------

show_use ()
{
    eval $(parse_params "[email protected]")
    # --
    echo "${ARGV[0]}" # print first unnamed param
    echo "${ARGV[1]}" # print second unnamed param
    echo "${ARGN[0]}" # print first named param
    echo "${ARG0[0]}" # print first option param (--force)
    echo "$anyparam"  # print --anyparam value
    echo "$k"         # print k=5 value
    echo "${multivalue[0]}" # print first value of multi-value
    echo "${multivalue[1]}" # print second value of multi-value
    [[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}

show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2
8

getopts funziona alla grande se # 1 lo hai installato e # 2 hai intenzione di eseguirlo sulla stessa piattaforma. OSX e Linux (ad esempio) si comportano diversamente a questo riguardo.

Ecco una soluzione (non getopts) che supporta flag uguali, non-uguali e booleani. Ad esempio potresti eseguire il tuo script in questo modo:

./script --arg1=value1 --arg2 value2 --shouldClean

# parse the arguments.
COUNTER=0
ARGS=("[email protected]")
while [ $COUNTER -lt $# ]
do
    arg=${ARGS[$COUNTER]}
    let COUNTER=COUNTER+1
    nextArg=${ARGS[$COUNTER]}

    if [[ $skipNext -eq 1 ]]; then
        echo "Skipping"
        skipNext=0
        continue
    fi

    argKey=""
    argVal=""
    if [[ "$arg" =~ ^\- ]]; then
        # if the format is: -key=value
        if [[ "$arg" =~ \= ]]; then
            argVal=$(echo "$arg" | cut -d'=' -f2)
            argKey=$(echo "$arg" | cut -d'=' -f1)
            skipNext=0

        # if the format is: -key value
        Elif [[ ! "$nextArg" =~ ^\- ]]; then
            argKey="$arg"
            argVal="$nextArg"
            skipNext=1

        # if the format is: -key (a boolean flag)
        Elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
            argKey="$arg"
            argVal=""
            skipNext=0
        fi
    # if the format has not flag, just a value.
    else
        argKey=""
        argVal="$arg"
        skipNext=0
    fi

    case "$argKey" in 
        --source-scmurl)
            SOURCE_URL="$argVal"
        ;;
        --dest-scmurl)
            DEST_URL="$argVal"
        ;;
        --version-num)
            VERSION_NUM="$argVal"
        ;;
        -c|--clean)
            CLEAN_BEFORE_START="1"
        ;;
        -h|--help|-help|--h)
            showUsage
            exit
        ;;
    esac
done
8
vangorra

EasyOptions non richiede alcuna analisi:

## Options:
##   --verbose, -v  Verbose mode
##   --output=FILE  Output filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "output file is ${output}"
    echo "${arguments[@]}"
fi
7
Renato Silva

Ecco come faccio in una funzione per evitare di far scappare i getopts nello stesso momento da qualche parte più in alto nello stack:

function waitForWeb () {
   local OPTIND=1 OPTARG OPTION
   local Host=localhost port=8080 proto=http
   while getopts "h:p:r:" OPTION; do
      case "$OPTION" in
      h)
         Host="$OPTARG"
         ;;
      p)
         port="$OPTARG"
         ;;
      r)
         proto="$OPTARG"
         ;;
      esac
   done
...
}
6
akostadinov

Mi piacerebbe offrire la mia versione di analisi delle opzioni, che consente quanto segue:

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

Permette anche questo (potrebbe essere indesiderato):

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

Devi decidere prima dell'uso se = deve essere usato su un'opzione o no. Questo per mantenere pulito il codice (ish).

while [[ $# > 0 ]]
do
    key="$1"
    while [[ ${key+x} ]]
    do
        case $key in
            -s*|--stage)
                STAGE="$2"
                shift # option has parameter
                ;;
            -w*|--workfolder)
                workfolder="$2"
                shift # option has parameter
                ;;
            -e=*)
                EXAMPLE="${key#*=}"
                break # option has been fully handled
                ;;
            *)
                # unknown option
                echo Unknown option: $key #1>&2
                exit 10 # either this: my preferred way to handle unknown options
                break # or this: do this to signal the option has been handled (if exit isn't used)
                ;;
        esac
        # prepare for next option in this key, if any
        [[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
    done
    shift # option(s) fully processed, proceed to next input argument
done
4
galmok

Si noti che getopt(1) è stato un breve errore vivente di AT & T.

getopt è stato creato nel 1984 ma era già stato seppellito nel 1986 perché non era veramente utilizzabile.

Una prova del fatto che getopt è superata è che la pagina man di getopt(1) menziona ancora "$*" invece di "[email protected]", che è stata aggiunta alla Bourne Shell nel 1986 insieme alla getopts(1) Shell incorporata per trattare argomenti con spazi interni.

BTW: se sei interessato ad analizzare lunghe opzioni negli script di Shell, potrebbe essere interessante sapere che l'implementazione getopt(3) da libc (Solaris) e ksh93 hanno entrambe aggiunto un'implementazione di opzione lunga uniforme che supporta opzioni lunghe come alias per le opzioni brevi. Ciò causa ksh93 e Bourne Shell per implementare un'interfaccia uniforme per le opzioni lunghe tramite getopts.

Un esempio di opzioni lunghe prese dalla pagina man di Bourne Shell:

getopts "f:(file)(input-file)o:(output-file)" OPTX "[email protected]"

mostra per quanto tempo gli alias di opzione possono essere utilizzati sia in Bourne Shell che in ksh93.

Vedi la pagina man di una recente Bourne Shell:

http://schillix.sourceforge.net/man/man1/bosh.1.html

e la pagina man per getopt (3) da OpenSolaris:

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

e infine, la pagina man getopt (1) per verificare l'obsoleto $ *:

http://schillix.sourceforge.net/man/man1/getopt.1.html

4
schily

Supponiamo di creare uno script Shell denominato test_args.sh come segue

#!/bin/sh
until [ $# -eq 0 ]
do
  name=${1:1}; shift;
  if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi  
done
echo "year=$year month=$month day=$day flag=$flag"

Dopo aver eseguito il seguente comando:

sh test_args.sh  -year 2017 -flag  -month 12 -day 22 

L'output sarebbe:

year=2017 month=12 day=22 flag=true
3
John

Mescolando argomenti posizionali e basati su flag

--param = arg (uguale delimitato)

Mixaggio automatico di flag tra argomenti posizionali:

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

può essere realizzato con un approccio abbastanza conciso:

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   param=${!pointer}
   if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      case $param in
         # paramter-flags with arguments
         -e=*|--environment=*) environment="${param#*=}";;
                  --another=*) another="${param#*=}";;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
         || set -- ${@:((pointer + 1)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

--param arg (spazio delimitato)

È sempre più chiaro non mischiare stili --flag=value e --flag value.

./script.sh dumbo 127.0.0.1 --environment production -q -d

Questo è un po 'difficile da leggere, ma è ancora valido

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

Fonte

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      param=${!pointer}
      ((pointer_plus = pointer + 1))
      slice_len=1

      case $param in
         # paramter-flags with arguments
         -e|--environment) environment=${!pointer_plus}; ((slice_len++));;
                --another) another=${!pointer_plus}; ((slice_len++));;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
         || set -- ${@:((pointer + $slice_len)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2
2
Mark Fox

Ho scritto un aiuto bash per scrivere un bel strumento di bash

progetto home: https://gitlab.mbedsys.org/mbedsys/bashopts

esempio:

#!/bin/bash -ei

# load the library
. bashopts.sh

# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"

# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"

# Parse arguments
bashopts_parse_args "[email protected]"

# Process argument
bashopts_process_args

darà aiuto:

NAME:
    ./example.sh - This is myapp tool description displayed on help message

USAGE:
    [options and commands] [-- [extra args]]

OPTIONS:
    -h,--help                          Display this help
    -n,--non-interactive true          Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
    -f,--first "John"                  First name - [$first_name] (type:string, default:"")
    -l,--last "Smith"                  Last name - [$last_name] (type:string, default:"")
    --display-name "John Smith"        Display name - [$display_name] (type:string, default:"$first_name $last_name")
    --number 0                         Age - [$age] (type:number, default:0)
    --email                            Email adress - [$email_list] (type:string, default:"")

godere :)

2
Emeric Verschuur

Ecco il mio approccio - usando regexp.

  • nessun getopts
  • gestisce il blocco di parametri brevi -qwerty
  • gestisce i parametri brevi -q -w -e
  • gestisce le opzioni lunghe --qwerty
  • è possibile passare l'attributo a un'opzione breve o lunga (se si utilizza il blocco di opzioni brevi, l'attributo è collegato all'ultima opzione)
  • puoi usare spazi o = per fornire attributi, ma attribuire corrispondenze fino a incontrare trattino + spazio "delimitatore", quindi in --q=qwe tyqwe ty è un attributo
  • gestisce il mix di tutti sopra così -o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute è valido

script:

#!/usr/bin/env sh

help_menu() {
  echo "Usage:

  ${0##*/} [-h][-l FILENAME][-d]

Options:

  -h, --help
    display this help and exit

  -l, --logfile=FILENAME
    filename

  -d, --debug
    enable debug
  "
}

parse_options() {
  case $opt in
    h|help)
      help_menu
      exit
     ;;
    l|logfile)
      logfile=${attr}
      ;;
    d|debug)
      debug=true
      ;;
    *)
      echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
      exit 1
  esac
}
[email protected]

until [ "$options" = "" ]; do
  if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
    if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
      opt=${BASH_REMATCH[3]}
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    Elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
      pile=${BASH_REMATCH[4]}
      while (( ${#pile} > 1 )); do
        opt=${pile:0:1}
        attr=""
        pile=${pile/${pile:0:1}/}
        parse_options
      done
      opt=$pile
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    else # leftovers that don't match
      opt=${BASH_REMATCH[10]}
      options=""
    fi
    parse_options
  fi
done
2
a_z

Soluzione che conserva argomenti non gestiti. Demo Incluso.

Ecco la mia soluzione. È MOLTO flessibile e diverso dagli altri, non dovrebbe richiedere pacchetti esterni e gestisce gli argomenti avanzati in modo pulito.

L'utilizzo è: ./myscript -flag flagvariable -otherflag flagvar2

Tutto quello che devi fare è modificare la linea dei tag validi. Prepone un trattino e cerca tutti gli argomenti. Quindi definisce l'argomento successivo come il nome della bandiera, ad es.

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

Il codice principale (versione breve, dettagliata con esempi più avanti, anche una versione con errore):

#!/usr/bin/env bash
#Shebang.io
validflags="rate time number"
count=1
for arg in [email protected]
do
    match=0
    argval=$1
    for flag in $validflags
    do
        sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers

La versione dettagliata con demo echo incorporate:

#!/usr/bin/env bash
#Shebang.io
rate=30
time=30
number=30
echo "all args
[email protected]"
validflags="rate time number"
count=1
for arg in [email protected]
do
    match=0
    argval=$1
#   argval=$(echo [email protected] | cut -d ' ' -f$count)
    for flag in $validflags
    do
            sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done

#Cleanup then restore the leftovers
echo "pre final clear args:
[email protected]"
shift $#
echo "post final clear args:
[email protected]"
set -- $leftovers
echo "all post set args:
[email protected]"
echo arg1: $1 arg2: $2

echo leftovers: $leftovers
echo rate $rate time $time number $number

Finale, questo errore se viene passato un argomento non valido.

#!/usr/bin/env bash
#Shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in [email protected]
do
    argval=$1
    match=0
        if [ "${argval:0:1}" == "-" ]
    then
        for flag in $validflags
        do
                sflag="-"$flag
            if [ "$argval" == "$sflag" ]
            then
                declare $flag=$2
                match=1
            fi
        done
        if [ "$match" == "0" ]
        then
            echo "Bad argument: $argval"
            exit 1
        fi
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers

Pro: quello che fa, gestisce molto bene. Conserva argomenti inutilizzati che molte altre soluzioni qui non hanno. Permette anche di chiamare le variabili senza essere definite a mano nello script. Permette anche la prepopolazione di variabili se non viene fornito alcun argomento corrispondente. (Vedi esempio dettagliato).

Contro: non è possibile analizzare una singola stringa di argomento complessa, ad es. -xcvf elaborerebbe come un singolo argomento. Si potrebbe in qualche modo scrivere codice aggiuntivo nel mio che però aggiunge questa funzionalità. 

2
Noah

Un'altra soluzione senza getopt [s], POSIX, vecchio stile Unix

Simile a la soluzione che Bruno Bronosky ha pubblicato questa qui è una senza l'uso di getopt(s).

La principale caratteristica di differenziazione della mia soluzione è che consente di avere opzioni concatenate insieme proprio come tar -xzf foo.tar.gz è uguale a tar -x -z -f foo.tar.gz. E proprio come in tar, ps ecc. Il trattino principale è opzionale per un blocco di opzioni brevi (ma può essere facilmente modificato). Sono supportate anche le opzioni lunghe (ma quando un blocco inizia con uno, sono richiesti due trattini principali).

Codice con opzioni di esempio

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from [email protected][se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  Elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

Per l'esempio di utilizzo, vedere gli esempi più avanti.

Posizione delle opzioni con argomenti

Per quello che vale, le opzioni con argomenti non sono le ultime (solo le opzioni lunghe devono essere). Quindi, mentre ad es. in tar (almeno in alcune implementazioni) le opzioni f devono essere le ultime perché il nome del file segue (tar xzf bar.tar.gz funziona ma tar xfz bar.tar.gz non lo fa) questo non è il caso qui (vedere gli esempi successivi).

Più opzioni con argomenti

Come ulteriore bonus, i parametri delle opzioni vengono consumati nell'ordine delle opzioni in base ai parametri con le opzioni richieste. Basta guardare l'output del mio script qui con la riga di comando abc X Y Z (o -abc X Y Z):

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

Opzioni lunghe concatenate pure

Inoltre, puoi anche avere opzioni lunghe nel blocco opzione dato che si verificano per ultimi nel blocco. Quindi le seguenti righe di comando sono tutte equivalenti (incluso l'ordine in cui vengono elaborate le opzioni e i relativi argomenti):

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

Tutti questi portano a:

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

Non in questa soluzione

Argomenti opzionali

Le opzioni con argomenti facoltativi dovrebbero essere possibili con un po 'di lavoro, ad es. guardando in avanti se c'è un blocco senza un trattino; l'utente dovrebbe quindi mettere un trattino davanti a ogni blocco che segue un blocco con un parametro che ha un parametro facoltativo. Forse questo è troppo complicato per comunicare all'utente in modo migliore, basta richiedere un trattino principale del tutto in questo caso.

Le cose diventano ancora più complicate con più parametri possibili. Vorrei sconsigliare di fare in modo che le opzioni cerchino di essere intelligenti determinando se un argomento potrebbe essere utile o meno (ad esempio con un'opzione si prende solo un numero come argomento opzionale) perché questo potrebbe rompersi in futuro.

Personalmente preferisco opzioni aggiuntive invece di argomenti opzionali.

Argomenti di opzioni introdotti con un segno di uguale

Proprio come con gli argomenti opzionali, non ne sono un fan (a proposito, c'è una discussione per discutere i pro/contro dei diversi stili di parametro?) Ma se vuoi questo probabilmente potresti implementarlo da solo come fatto a http: //mywiki.wooledge.org/BashFAQ/035#Manual_loop con una dichiarazione del caso --long-with-arg=?* e quindi stripping del segno di uguale (questo è BTW il sito che dice che fare la concatenazione dei parametri è possibile con qualche sforzo ma "lasciato [it] come un esercizio per il lettore "che mi ha spinto a prenderli alla loro Parola, ma sono partito da zero).

Altre note

Conforme a POSIX, funziona anche su configurazioni Busybox antiche che ho dovuto gestire (con ad esempio cut, head e getopts mancanti).

1
phk

Anche questo potrebbe essere utile sapere, puoi impostare un valore e se qualcuno fornisce un input, sostituisci il valore predefinito con quel valore. 

myscript.sh -f ./serverlist.txt o solo ./myscript.sh (e prende i valori predefiniti)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift

    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done


    echo "SERVER LIST   = ${SERVER_FILE_LIST}"
1
Mike Q

Ecco la mia soluzione migliorata della risposta di Bruno Bronosky usando array variabili.

ti consente di mixare i parametri e di darti un array di parametri preservando l'ordine senza le opzioni

#!/bin/bash

echo [email protected]

PARAMS=()
SOFT=0
SKIP=()
for i in "[email protected]"
do
case $i in
    -n=*|--skip=*)
    SKIP+=("${i#*=}")
    ;;
    -s|--soft)
    SOFT=1
    ;;
    *)
        # unknown option
        PARAMS+=("$i")
    ;;
esac
done
echo "SKIP            = ${SKIP[@]}"
echo "SOFT            = $SOFT"
    echo "Parameters:"
    echo ${PARAMS[@]}

Produrrà per esempio:

$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP            = .c .obj
SOFT            = 1
Parameters:
parameter somefile
1
Masadow

Espandendo la risposta di @ bruno-bronosky, ho aggiunto un "preprocessore" per gestire alcune formattazioni comuni:

  • Espande --longopt=val in --longopt val
  • Espande -xyz in -x -y -z
  • Supporta -- per indicare la fine dei flag
  • Mostra un errore per le opzioni inattese
  • Commutatore di opzioni compatto e di facile lettura
#!/bin/bash

# Report usage
usage() {
  echo "Usage:"
  echo "$(basename $0) [options] [--] [file1, ...]"

  # Optionally exit with a status code
  if [ -n "$1" ]; then
    exit "$1"
  fi
}

invalid() {
  echo "ERROR: Unrecognized argument: $1" >&2
  usage 1
}

# Pre-process options to:
# - expand -xyz into -x -y -z
# - expand --longopt=arg into --longopt arg
ARGV=()
END_OF_OPT=
while [[ $# -gt 0 ]]; do
  arg="$1"; shift
  case "${END_OF_OPT}${arg}" in
    --) ARGV+=("$arg"); END_OF_OPT=1 ;;
    --*=*)ARGV+=("${arg%%=*}" "${arg#*=}") ;;
    --*) ARGV+=("$arg"); END_OF_OPT=1 ;;
    -*) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;;
    *) ARGV+=("$arg") ;;
  esac
done

# Apply pre-processed options
set -- "${ARGV[@]}"

# Parse options
END_OF_OPT=
POSITIONAL=()
while [[ $# -gt 0 ]]; do
  case "${END_OF_OPT}${1}" in
    -h|--help)      usage 0 ;;
    -p|--password)  shift; PASSWORD="$1" ;;
    -u|--username)  shift; USERNAME="$1" ;;
    -n|--name)      shift; names+=("$1") ;;
    -q|--quiet)     QUIET=1 ;;
    -C|--copy)      COPY=1 ;;
    -N|--notify)    NOTIFY=1 ;;
    --stdin)        READ_STDIN=1 ;;
    --)             END_OF_OPT=1 ;;
    -*)             invalid "$1" ;;
    *)              POSITIONAL+=("$1") ;;
  esac
  shift
done

# Restore positional parameters
set -- "${POSITIONAL[@]}"
1
jchook

Questo esempio mostra come usare getopt e eval e HEREDOC e shift per gestire parametri brevi e lunghi con e senza un valore richiesto che segue. Anche l'istruzione switch/case è concisa e facile da seguire.

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, don't change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "[email protected]")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <<-EOM
   num=$num_str
   time=$time_str
   verbose=$verbose
   dryrun=$dryrun
EOM
fi

# The rest of your script below

Le righe più significative dello script sopra sono queste:

OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "[email protected]")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Breve, al punto, leggibile e gestisce praticamente tutto (IMHO).

Spero che aiuti qualcuno.

1
phyatt

Usa il modulo "argomenti" da bash-modules

Esempio:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "[email protected]" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"

Voglio inviare il mio progetto: https://github.com/flyingangel/argparser

source argparser.sh
parse_args "[email protected]"

Semplice come quella. L'ambiente verrà popolato con variabili con lo stesso nome degli argomenti

1
Thanh Trung

La prima risposta a questa domanda mi è sembrata un po 'problematica quando l'ho provata: ecco la mia soluzione che ho trovato più solida:

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi
1
Daniel Bigham

Semplice e facile da modificare, i parametri possono essere in qualsiasi ordine. questo può essere modificato per assumere parametri in qualsiasi forma (-a, --a, a, ecc.).

for arg in "[email protected]"
do
   key=$(echo $arg | cut -f1 -d=)`
   value=$(echo $arg | cut -f2 -d=)`
   case "$key" in
        name|-name)      read_name=$value;;
        id|-id)          read_id=$value;;
        *)               echo "I dont know what to do with this"
   ease
done
0
terijo001