2015-02-18 25 views
6

verwende ich folgende Zeilen (hoffe, das ist beste Praxis, wenn nicht korrigiert mich bitte) Befehlszeilenoptionen zu handhaben:Bash - getopts - Massen Argumente (Operanden) an erster Stelle in Argument Befehlszeile übergeben

#!/usr/bin/bash 

read -r -d '' HELP <<EOF 
OPTIONS: 
-c Enable color output 
-d Enable debug output 
-v Enable verbose output 
-n Only download files (mutually exclusive with -r) 
-r Only remove files (mutually exclusive with -n) 
-h Display this help 
EOF 

# DECLARE VARIABLES WITH DEFAULT VALUES 
color=0 
debug=0 
verbose=0 
download=0 
remove=0 


OPTIND=1    # Reset in case getopts has been used previously in the shell 
invalid_options=();  # Array for invalid options 

while getopts ":cdvnrh" opt; do 
    echo "Actual opt: $opt" 
    case $opt in 
    c) 
     color=1 
     ;; 
    d) 
     debug=1 
     ;; 
    v) 
     verbose=1 
     ;; 
    n) 
     download=1 
     ;; 
    r) 
     remove=1 
     ;; 
    h) 
     echo "$HELP" 
     exit 1 
     ;; 
    \?) 
     invalid_options+=($OPTARG) 
     ;; 
    *) 
     invalid_options+=($OPTARG) 
     ;; 
    esac 
done 

# HANDLE INVALID OPTIONS 
if [ ${#invalid_options[@]} -ne 0 ]; then 
    echo "Invalid option(s):" >&2 
    for i in "${invalid_options[@]}"; do 
    echo $i >&2 
    done 
    echo "" >&2 
    echo "$HELP" >&2 
    exit 1 
fi 

# SET $1 TO FIRST MASS ARGUMENT, $2 TO SECOND MASS ARGUMENT ETC 
shift $((OPTIND - 1)) 

# HANDLE CORRECT NUMBER OF MASS OPTIONS 
if [ $# -ne 2 ]; then 
    echo "Correct number of mass arguments are 2" 
    echo "" >&2 
    echo "$HELP" >&2 
    exit 1 
fi 


# HANDLE MUTUALLY EXCLUSIVE OPTIONS 
if [ $download -eq 1 ] && [ $remove -eq 1 ]; then 
    echo "Options for download and remove are mutually exclusive" >&2 
    echo "$HELP" >&2 
    exit 1 
fi 



echo "color: $color" 
echo "debug: $debug" 
echo "verbose: $verbose" 
echo "download: $download" 
echo "remove: $remove" 
echo "\$1:  $1" 
echo "\$2:  $2" 

Wenn ich nenne das Skript Art und Weise, dass mass arguments (diejenigen, die nicht Schalter oder Argumente für Schalter sind) sind letzte Argumente alles korrekt funktioniert:

$ ./getopts.sh -c -d -v -r a b 
Actual opt: c 
Actual opt: d 
Actual opt: v 
Actual opt: r 
color: 1 
debug: 1 
verbose: 1 
download: 0 
remove: 1 
$1:  a 
$2:  b 

Das Problem ist, wenn ich das Skript aufgerufen werden soll, so dass die Masse Argumente ersten sind (oder irgendwo in der Mitte von Schaltern, die d o verwenden Argumente nicht)

$ ./getopts.sh a b -c -d -v -r 
Correct number of mass arguments are 2 

OPTIONS: 
-c Enable color output 
-d Enable debug output 
-v Enable verbose output 
-n Only download files (mutually exclusive with -r) 
-r Only remove files (mutually exclusive with -d) 
-h Display this help 

oder

$ ./getopts.sh -c a b -d -v -r 
Actual opt: c 
Correct number of mass arguments are 2 

OPTIONS: 
-c Enable color output 
-d Enable debug output 
-v Enable verbose output 
-n Only download files (mutually exclusive with -r) 
-r Only remove files (mutually exclusive with -d) 
-h Display this help 

Ich denke, dies in Ordnung sein sollte nach (POSIX) Standards, da Syntax folgt das ist im Grunde das gleiche funktioniert wie auf meinem System erwartet:

$ cp test1/ test2/ -r 
$ cp test1/ -r test2/ 

ich habe über das Internet suchen, aber nur, was mir in der Nähe Problem war, this one im Zusammenhang mit C.

+0

Ihre Beobachtungen sind korrekt: getopts gibt einen Exit-Status zurück, wenn das erste Nicht-Optionsargument gefunden wird, sodass die while-Schleife endet. Ich glaube, [getopt'] (http://man.cx/getopt) kann so flexibel arbeiten, wie Sie wollen. –

+0

@glennjackman gibt es Empfehlungen für die Verwendung von getopts über getopt. AFAIK getopt gilt als obsolet. –

+0

Ich glaube nicht, dass getopt als veraltet gilt. getopts ist einfacher zu benutzen (IMO: es ist, was ich benutze), aber es zwingt Sie, die Optionen vor die erforderlichen Parameter zu setzen, und Sie können --longoptions nicht verwenden. –

Antwort

1

Bash bietet zwei Methoden zum Parsen von Argumenten.

Der integrierte Befehl getopts ist ein neuerer, einfach zu verwendender Mechanismus zum Analysieren von Argumenten, aber er ist nicht sehr flexibel. getopts erlaubt nicht, Optionen und Massenargumente zu mischen.

Der externe Befehl getopt ist ein älterer und komplexerer Mechanismus zum Analysieren von Argumenten. Es erlaubt lange/kurze Optionen und die GNU-Erweiterung erlaubt es, Optionen und Massenargumente zu mischen.

3

getopts bricht die while-Schleife automatisch ab, sobald ein Nicht-Strich-Parameter erkannt wird (ohne das Argument Strich Parameter, die Argumente übernehmen). Der POSIX Standard ist, dass die gestrichelten Parameter zuerst kommen und dann Dateien haben. Es gibt auch nichts davon -- und + Mist. Es ist schlicht und einfach.

Linux ist jedoch nicht Unix- oder POSIX-kompatibel. Es ist nur in der Natur der GNU-Dienstprogramme, "besser" als die Standard-Unix-Dienstprogramme zu sein. Mehr Funktionen, mehr Optionen und etwas anderes.

Unter Linux können Befehlszeilenparameter nach Dateien in vielen GNU-Dienstprogrammen kommen.

Zum Beispiel:

$ cp -R foo bar 

Arbeit auf meinem Unix-zertifizierten Mac OS X und Linux, aber

$ cp foo bar -R 

funktioniert nur auf Linux.

Wenn Sie möchten, dass getopts wie viele Linux-Dienstprogramme arbeiten, müssen Sie ein kleines bisschen Arbeit tun.

Zuerst müssen Sie Ihre Argumente selbst verarbeiten, und nicht von $OPTIND abhängen, um sie zu analysieren. Sie müssen auch überprüfen, ob Sie ein Argument haben.

Ich kam dies als ein Beispiel für das, was Sie wollen.

#! /bin/bash 

while [[ $* ]] 
do 
    OPTIND=1 
    echo $1 
    if [[ $1 =~ ^- ]] 
    then 
     getopts :a:b:cd parameter 
     case $parameter in 
      a) echo "a" 
       echo "the value is $OPTARG" 
       shift 
       ;; 
      b) echo "b" 
       echo "the value is $OPTARG" 
       shift 
       ;; 
      c) echo "c" 
       ;; 
      d) echo "d" 
       ;; 
      *) echo "This is an invalid argument: $parameter" 
       ;; 
     esac 
    else 
     other_arguments="$other_arguments $1" 
    fi 
    shift 
done 
echo "$other_arguments" 

Ich Schleife jetzt solange $* gesetzt ist. (Vielleicht sollte ich [email protected] verwenden?) Ich muss eine shift am Ende der Schleife tun. Ich setze auch jedesmal $OPTIND auf 1 zurück, weil ich die Argumente von mir selbst verschiebe. $OPTARG ist immer noch eingestellt, aber ich muss noch eine shift tun, um sicherzustellen, dass alles funktioniert.

Ich muss auch überprüfen, ob ein Argument mit einem Bindestrich oder nicht mit einem regulären Ausdruck in meiner if-Anweisung beginnt.

Grundlegende Tests zeigen, dass es funktioniert, aber ich kann nicht sagen, es ist fehlerfrei, aber es gibt Ihnen eine Idee, wie Sie mit Ihrem Programm umgehen müssen.

Es gibt noch viel Leistung, die Sie von getopts bekommen, aber es dauert ein bisschen mehr Arbeit.