2015-02-18 10 views
6

Używam następujące linie (nadzieję, że to jest najlepsze praktyki, jeśli nie poprawcie mnie proszę) do obsługi opcji wiersza polecenia:Bash - getopts - masa argumenty (operandów) na pierwszym miejscu w linii argumentu polecenia przechodzące

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

Jeśli wzywam drogę skryptu mass arguments (te, które nie są przełączniki lub argumenty dla przełączników) są ostatnie argumenty wszystko działa poprawnie:

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

problem jest, gdy chcę wywołać skrypt więc argumenty masowe są pierwsze (lub gdzieś w środku przełączników, które d • Nie używać argumentów)

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

lub

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

myślę, że to powinno być OK według (POSIX) normy, bo po składni, która jest w zasadzie taka sama działa zgodnie z oczekiwaniami w moim systemie:

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

mam szukać w Internecie, ale jedyną rzeczą, która była blisko mojego problemu było this one związane C

+0

Twoje obserwacje są poprawne: getopts zwraca kod zakończenia, gdy napotkany zostanie pierwszy argument nie będący opcją, więc kończy się pętla while. Wierzę, że ['getopt'] (http://man.cx/getopt) może działać w bardziej elastyczny sposób. –

+0

@glennjackman istnieją zalecenia dotyczące używania getopts przez getopt. AFAIK getopt jest uważany za przestarzały. –

+0

Nie wierzę, że getopt jest przestarzały. getopts jest łatwiejszy w użyciu (IMO: to jest to, czego używam), ale zmusza cię do umieszczenia opcji przed wymaganymi parametrami i nie możesz użyć opcji -longoptions. –

Odpowiedz

1

Bash udostępnia dwie metody analizy argumentów.

Wbudowana komenda getopts jest nowszym, łatwym w użyciu mechanizmem analizowania argumentów, ale nie jest zbyt elastyczna. getopts nie pozwala na mieszanie opcji i argumentów masowych.

Zewnętrzne polecenie getopt jest starszym i bardziej złożonym mechanizmem do analizowania argumentów. Pozwala na długie/krótkie opcje, a rozszerzenie gnu pozwala na mieszanie opcji i argumentów masowych.

3

getopts automatycznie przerywa pętlę while, gdy tylko wykryje parametr inny niż kreska (nie uwzględniając argumentów podanych dla parametrów dash, które pobierają argumenty). Standard POSIX ma mieć parametry przerywane jako pierwsze, a następnie mieć plików. Nie ma też żadnej z tych bzdur. To proste i proste.

Jednak Linux nie jest zgodny z Unix lub POSIX. Z samej natury narzędzia GNU można "lepiej" niż standardowe narzędzia Unix. Więcej funkcji, więcej opcji i obsługa rzeczy nieco inaczej.

W systemie Linux parametry wiersza poleceń mogą pochodzić od po plikach w wielu programach GNU.

Na przykład:

$ cp -R foo bar 

działa na moim certyfikatem Unix Mac OS X i Linux, jednak

$ cp foo bar -R 

Tylko działa na Linux.

Jeśli chcesz, aby getopts działało jak wiele programów linuksowych, musisz wykonać trochę pracy.

Najpierw musisz przetworzyć argumenty samodzielnie i nie polegać na $OPTIND, aby je przeanalizować. Musisz również sprawdzić, czy masz argument.

Wpadłem na to jako przykład robienia tego, co chcesz.

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

Teraz pętlę, dopóki ustawiony jest $*. (Może powinienem użyć [email protected]?) Muszę zrobić shift na końcu pętli. Również za każdym razem resetuję $OPTIND do 1, ponieważ odsuwam argumenty od siebie. $OPTARG jest nadal ustawiony, ale muszę zrobić kolejny shift, aby upewnić się, że wszystko działa.

Muszę również sprawdzić, czy argument zaczyna się od myślnika, czy nie używać wyrażenia regularnego w mojej instrukcji if.

Podstawowe testowanie pokazuje, że działa, ale nie mogę powiedzieć, że jest wolne od błędów, ale daje ci to pojęcie, jak sobie radzisz z programem.

Wciąż jest dużo energii, którą otrzymujesz od getopts, ale wymaga to trochę więcej pracy.

Powiązane problemy