getopt
e getopts
sono bestie diverse, e le persone sembrano avere un po 'di incomprensioni su ciò che fanno. getopts
è un comando integrato bash
per elaborare le opzioni della riga di comando in un ciclo e assegnare ciascuna opzione trovata e valore a sua volta alle variabili integrate, in modo da poterle ulteriormente elaborare. getopt
, tuttavia, è un programma di utilità esterno e in realtà non elabora le opzioni per te come fanno bash getopts
, il Getopt
modulo Perl o Python optparse
/ module argparse
. Tutto ciò che getopt
fa è canonicalizzare le opzioni che vengono passate, ovvero convertirle in un modulo più standard, in modo che sia più facile per uno script shell elaborarle. Ad esempio, un'applicazione di getopt
potrebbe convertire quanto segue:
myscript -ab infile.txt -ooutfile.txt
in questo:
myscript -a -b -o outfile.txt infile.txt
Devi eseguire tu stesso l'elaborazione effettiva. Non è necessario utilizzare getopt
affatto se si applicano varie restrizioni sul modo in cui è possibile specificare le opzioni:
- metti solo un'opzione per argomento;
- tutte le opzioni vanno prima di qualsiasi parametro posizionale (cioè argomenti non di opzione);
- per le opzioni con valori (ad es.
-o
sopra), il valore deve andare come argomento separato (dopo uno spazio).
Perché usare getopt
invece di getopts
? Il motivo di base è che solo GNU getopt
offre supporto per le opzioni della riga di comando con nome lungo. 1 (GNU getopt
è l'impostazione predefinita su Linux. Mac OS X e FreeBSD sono forniti di base e non molto utili getopt
, ma la versione GNU può essere installata; vedi sotto).
Ad esempio, ecco un esempio dell'uso di GNU getopt
, da un mio script chiamato javawrap
:
# NOTE: This requires GNU getopt. On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \
-n 'javawrap' -- "$@"`
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
case "$1" in
-v | --verbose ) VERBOSE=true; shift ;;
-d | --debug ) DEBUG=true; shift ;;
-m | --memory ) MEMORY="$2"; shift 2 ;;
--debugfile ) DEBUGFILE="$2"; shift 2 ;;
--minheap )
JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
--maxheap )
JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
-- ) shift; break ;;
* ) break ;;
esac
done
Ciò consente di specificare opzioni simili --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"
o simili. L'effetto della chiamata a getopt
è di canonicalizzare le opzioni in --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"
modo da poterle elaborare più facilmente. La citazione intorno "$1"
ed "$2"
è importante in quanto garantisce che gli argomenti con spazi siano gestiti correttamente.
Se elimini le prime 9 righe (tutto su attraverso la eval set
riga), il codice continuerà a funzionare ! Tuttavia, il tuo codice sarà molto più selettivo in quale tipo di opzioni accetta: In particolare, dovrai specificare tutte le opzioni nel modulo "canonico" sopra descritto. Con l'uso di getopt
, tuttavia, è possibile raggruppare opzioni a lettera singola, utilizzare forme più brevi non ambigue di opzioni lunghe, utilizzare lo stile --file foo.txt
o --file=foo.txt
, utilizzare lo stile -m 4096
o -m4096
, mescolare opzioni e non opzioni in qualsiasi ordine, ecc. getopt
genera inoltre un messaggio di errore se vengono rilevate opzioni non riconosciute o ambigue.
NOTA : Esistono in realtà due versioni totalmente diverse di getopt
, base getopt
e GNU getopt
, con caratteristiche diverse e convenzioni di chiamata diverse. 2 Basic getopt
è piuttosto rotto: non solo non gestisce le opzioni lunghe, ma non può nemmeno gestire spazi incorporati all'interno di argomenti o argomenti vuoti, mentre lo getopts
fa bene. Il codice sopra non funzionerà in base getopt
. GNU getopt
è installato di default su Linux, ma su Mac OS X e FreeBSD deve essere installato separatamente. Su Mac OS X, installa MacPorts ( http://www.macports.org ) e quindi esegui sudo port install getopt
l'installazione di GNU getopt
(di solito in /opt/local/bin
) e assicurati che /opt/local/bin
sia nel percorso della shell prima di/usr/bin
. Su FreeBSD, installa misc/getopt
.
Una guida rapida alla modifica del codice di esempio per il tuo programma: Delle prime righe, tutto è "boilerplate" che dovrebbe rimanere lo stesso, tranne la linea che chiama getopt
. È necessario modificare il nome del programma dopo -n
, specificare le opzioni brevi dopo -o
e le opzioni lunghe dopo --long
. Inserisci i due punti dopo le opzioni che assumono un valore.
Infine, se vedi il codice che ha solo set
invece di eval set
, è stato scritto per BSD getopt
. Dovresti cambiarlo per usare lo eval set
stile, che funziona bene con entrambe le versioni di getopt
, mentre la pianura set
non funziona bene con GNU getopt
.
1 In realtà, getopts
in ksh93
supporti opzioni lunga di nome, ma questo guscio non è usato spesso come bash
. In zsh
, utilizzare zparseopts
per ottenere questa funzionalità.
2 Tecnicamente, "GNU getopt
" è un termine improprio; questa versione è stata effettivamente scritta per Linux anziché per il progetto GNU. Tuttavia, segue tutte le convenzioni GNU e il termine "GNU getopt
" è comunemente usato (ad esempio su FreeBSD).