Używanie getopts do przetwarzania długich i krótkich opcji wiersza poleceń

Chcę mieć długie i krótkie formy opcji wiersza poleceń wywoływanych przy użyciu mojego skryptu powłoki.

Wiem, że getopts może być używany, ale tak jak w Perlu, nie byłem w stanie zrobić tego samego z powłoką.

Jakieś pomysły jak to zrobić, żebym mógł użyć opcji takich jak:

./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/

W powyższym przykładzie oba polecenia znaczą to samo dla mojej powłoki, ale używając getopts, nie byłem w stanie ich zaimplementować?

Author: codeforester, 2008-12-31

30 answers

Istnieją trzy implementacje, które można rozważyć:

  • Bash builtin getopts. Nie obsługuje to długich nazw opcji z prefiksem double-dash. Obsługuje tylko opcje jednoznakowe.

  • BSD Unix implementacja samodzielnej komendy getopt (której używa MacOS). Nie obsługuje to również długich opcji.

  • GNU implementacja standalone getopt. GNU getopt(3) (używany przez wiersz poleceńgetopt(1) on Linux) obsługuje parsowanie długich opcji.


Niektóre inne odpowiedzi pokazują rozwiązanie do używania wbudowanego bash getopts do naśladowania długich opcji. Rozwiązanie to tworzy krótką opcję, której znakiem jest" -". Więc dostajesz " -- " jako flagę. Następnie wszystko, co następuje, staje się OPTARG, a Ty testujesz OPTARG z zagnieżdżonym case.

To jest sprytne, ale ma zastrzeżenia:

  • getopts nie można wymusić specyfikacji opt. Nie może zwrócić błędów, jeśli użytkownik dostarcza nieprawidłową opcję. Podczas parsowania OPTARG musisz samodzielnie sprawdzać błędy.
  • OPTARG jest używany do długiej nazwy opcji, co komplikuje użycie, gdy sama opcja long Ma argument. Sam musisz to zakodować jako dodatkową sprawę.

Więc chociaż możliwe jest napisanie większej ilości kodu, aby obejść brak wsparcia dla długich opcji, jest to o wiele więcej pracy i częściowo podważa cel użycia parsera getopt w celu uproszczenia kod.

 315
Author: Bill Karwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-26 16:49:23

getopt i getopts są różnymi bestiami, a ludzie wydają się mieć trochę niezrozumienia tego, co robią. getopts jest wbudowanym poleceniem bash do przetwarzania opcji wiersza poleceń w pętli i przypisywania każdej znalezionej opcji i wartości z kolei do wbudowanych zmiennych, dzięki czemu można je dalej przetwarzać. getopt jest jednak zewnętrznym programem narzędziowym i nie przetwarza twoich opcji za Ciebie w sposób, w jaki np. bash getopts, Moduł Perl Getopt czy Python optparse/argparse Moduły zrób. Wszystko, co getopt robi, to canonicalize the options that are passed in - tzn. convert them to a more standard form, so that it ' s eately for a shell script to process them. Na przykład, aplikacja getopt może przekształcić:

myscript -ab infile.txt -ooutfile.txt

Do tego:

myscript -a -b -o outfile.txt infile.txt
Sam musisz to przetworzyć. Nie musisz używać getopt w ogóle, jeśli wprowadzasz różne ograniczenia dotyczące sposobu, w jaki możesz określić opcje:
  • dodaj tylko jedną opcję na argument;
  • wszystkie opcje przechodzą przed dowolnymi parametrami pozycyjnymi (tzn. argumentami nie będącymi opcjami);
  • dla opcji z wartościami (np. -o powyżej), wartość musi przejść jako oddzielny argument (po spacji).

Dlaczego używać getopt zamiast getopts? Podstawowym powodem jest to, że tylko GNU getopt daje wsparcie dla opcji wiersza poleceń o długiej nazwie.1 (GNU {[3] } jest domyślne w Linuksie. Mac OS X i FreeBSD są dostarczane z podstawową i niezbyt przydatną getopt, ale wersja GNU może być zainstalowane; patrz poniżej.)

Na przykład, oto przykład użycia GNU getopt, ze skryptu o nazwie 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

Pozwala to określić opcje takie jak --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt" lub podobne. Efektem wywołania do getopt jest kanoniczne zdefiniowanie opcji do --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt", dzięki czemu można je łatwiej przetworzyć. Cytowanie wokół "$1" i "$2" jest ważne, ponieważ zapewnia prawidłowe obchodzenie się z argumentami ze spacjami.

Jeśli usuniesz pierwsze 9 linijek (wszystko przez linię eval set), kod będzie nadal działał ! Jednak Twój kod będzie dużo bardziej wybierany pod kątem tego, jakie opcje akceptuje: w szczególności będziesz musiał podać wszystkie opcje w" kanonicznej " formie opisanej powyżej. Za pomocą getopt można jednak grupować opcje jednoliterowe, używać krótszych, nie wieloznacznych form długich opcji, używać stylu --file foo.txt lub --file=foo.txt, używać stylu -m 4096 lub -m4096, mieszać opcje i nie-opcje w dowolnej kolejności itp. getopt również wyjścia komunikat o błędzie w przypadku znalezienia nierozpoznanych lub niejednoznacznych opcji.

Uwaga : w rzeczywistości istnieją dwie całkowicie różne wersje getopt, basic getopt i GNU getopt, z różnymi funkcjami i różnymi konwencjami wywołania.2 Basic {[3] } jest dość zepsuty: nie tylko nie obsługuje długich opcji, ale także nie radzi sobie nawet z osadzonymi spacjami wewnątrz argumentów lub pustymi argumentami, podczas gdy getopts robi to dobrze. Powyższy kod nie będzie działał w basic getopt. GNU getopt jest domyślnie zainstalowany na Linuksie, ale na Mac OS X i FreeBSD musi być zainstalowany osobno. W systemie Mac OS X zainstaluj MacPorts (http://www.macports.org ), a następnie wykonaj sudo port install getopt, aby zainstalować GNU getopt (Zwykle do /opt/local/bin) i upewnij się, że /opt/local/bin jest w twojej ścieżce powłoki przed /usr/bin. Na FreeBSD zainstaluj misc/getopt.

Krótki poradnik jak zmodyfikować przykładowy kod dla własnego programu: z pierwszych kilku linii, wszystko jest" kotłem", który powinien pozostać taki sam, z wyjątkiem linia wywołująca getopt. Należy zmienić nazwę programu po -n, podać krótkie opcje po -o i długie opcje po --long. Umieść dwukropek po opcjach, które przyjmują wartość.

Wreszcie, jeśli widzisz kod, który ma tylko set zamiast eval set, został napisany dla BSD getopt. Powinieneś zmienić styl eval set, który działa dobrze z obiema wersjami getopt, podczas gdy zwykły set nie działa dobrze z GNU getopt.

1właściwie, getopts w ksh93 obsługuje opcje o długich nazwach, ale ta powłoka nie jest używana tak często jak bash. W zsh, użyj zparseopts, aby uzyskać tę funkcjonalność.

2technicznie rzecz biorąc," GNU getopt " jest błędną nazwą; ta wersja została napisana raczej dla Linuksa niż dla projektu GNU. Jest to jednak zgodne ze wszystkimi konwencjami GNU, a termin "GNU getopt" jest powszechnie używany (np. na FreeBSD).

 320
Author: Urban Vagabond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-12 05:11:52

Wbudowana w Bash funkcja getopts może być używana do analizy długich opcji poprzez umieszczenie znaku myślnika, po którym następuje dwukropek w optspec:

#!/usr/bin/env bash 
optspec=":hv-:"
while getopts "$optspec" optchar; do
    case "${optchar}" in
        -)
            case "${OPTARG}" in
                loglevel)
                    val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                    echo "Parsing option: '--${OPTARG}', value: '${val}'" >&2;
                    ;;
                loglevel=*)
                    val=${OPTARG#*=}
                    opt=${OPTARG%=$val}
                    echo "Parsing option: '--${opt}', value: '${val}'" >&2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h)
            echo "usage: $0 [-v] [--loglevel[=]<value>]" >&2
            exit 2
            ;;
        v)
            echo "Parsing option: '-${optchar}'" >&2
            ;;
        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done

Po skopiowaniu do pliku wykonywalnego name = getopts_test.sh w bieżącym katalogu roboczym , można wygenerować wyjście w postaci

$ ./getopts_test.sh
$ ./getopts_test.sh -f
Non-option argument: '-f'
$ ./getopts_test.sh -h
usage: code/getopts_test.sh [-v] [--loglevel[=]<value>]
$ ./getopts_test.sh --help
$ ./getopts_test.sh -v
Parsing option: '-v'
$ ./getopts_test.sh --very-bad
$ ./getopts_test.sh --loglevel
Parsing option: '--loglevel', value: ''
$ ./getopts_test.sh --loglevel 11
Parsing option: '--loglevel', value: '11'
$ ./getopts_test.sh --loglevel=11
Parsing option: '--loglevel', value: '11'

Oczywiście getopts nie wykonuje OPTERR sprawdzania ani parsowania argumentów opcji dla długich opcji. Powyższy fragment skryptu pokazuje, jak można to zrobić ręcznie. Podstawowa zasada działa również w Debian Almquist shell ("dash"). Uwaga na przypadek szczególny:

getopts -- "-:"  ## without the option terminator "-- " bash complains about "-:"
getopts "-:"     ## this works in the Debian Almquist shell ("dash")

Zauważ, że jako GreyCat od strony http://mywiki.wooledge.org/BashFAQ wskazuje, że ta sztuczka wykorzystuje niestandardowe zachowanie powłoki, które pozwala na połączenie opcji-argumentu (tj. nazwy pliku w "-f filename") z opcją (jak w"- ffilename"). Standard POSIX mówi, że musi być między nimi spacja, która w przypadku "-- longoption" zakończy parsowanie opcji i Zamień wszystkie longoptions na argumenty niebędące opcjami.

 213
Author: Arvid Requate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-10 22:58:51

Wbudowane polecenie getopts jest nadal, AFAIK, ograniczone tylko do opcji jednoznakowych.

Istnieje (lub kiedyś był) zewnętrzny program getopt, który przeorganizowałby zestaw opcji tak, aby łatwiej było je przeanalizować. Możesz dostosować ten projekt do obsługi długich opcji. Przykład użycia:

aflag=no
bflag=no
flist=""
set -- $(getopt abf: "$@")
while [ $# -gt 0 ]
do
    case "$1" in
    (-a) aflag=yes;;
    (-b) bflag=yes;;
    (-f) flist="$flist $2"; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*)  break;;
    esac
    shift
done

# Process remaining non-option arguments
...

Możesz użyć podobnego schematu za pomocą polecenia getoptlong.

Zauważ, że podstawową słabością zewnętrznego getopt programu jest trudność obsługi argumentów z przestrzenie w nich, a w zachowaniu tych przestrzeni dokładnie. To dlatego wbudowany getopts jest lepszy, choć Ograniczony faktem, że obsługuje tylko opcje jednoliterowe.

 148
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-31 14:46:59

Oto przykład, który faktycznie używa getopt z długimi opcjami:

aflag=no
bflag=no
cargument=none

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc: -l along,blong,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag="yes" ;;
    -b|--blong) bflag="yes" ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done
 78
Author: sme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-25 12:00:40

Długie opcje mogą być przetwarzane przez standard getopts wbudowany jako "argumenty" do - "opcja"

Jest to przenośna i natywna powłoka POSIX – nie są potrzebne żadne zewnętrzne programy ani bashismy.

Ten przewodnik implementuje długie opcje jako argumenty do opcji -, więc --alpha jest postrzegana przez getopts jako - z argumentem alpha i --bravo=foo jest postrzegana jako - z argumentem bravo=foo. Prawdziwy argument może być zebrany za pomocą prostego zamiennika: ${OPTARG#*=}.

W tym przykładzie, -b i -c (oraz ich długie formy, --bravo i --charlie) mają obowiązkowe argumenty. Argumenty długich opcji pojawiają się po znakach równości, np. --bravo=foo (ograniczniki spacji dla długich opcji byłyby trudne do zaimplementowania, patrz poniżej).

Ponieważ to wykorzystuje getopts wbudowane , To rozwiązanie obsługuje użycie jak cmd --bravo=foo -ac FILE (który ma połączone opcje -a i -c i przeplata długie opcje ze standardowymi opcjami) , podczas gdy większość innych odpowiedzi tutaj albo walczyć lub nie zrobić to.

die() { echo "$*" >&2; exit 2; }  # complain to STDERR and exit with error
needs_arg() { if [ -z "$OPTARG" ]; then die "No arg for --$OPT option"; fi; }

while getopts ab:c:-: OPT; do
  # support long options: https://stackoverflow.com/a/28466267/519360
  if [ "$OPT" = "-" ]; then   # long option: reformulate OPT and OPTARG
    OPT="${OPTARG%%=*}"       # extract long option name
    OPTARG="${OPTARG#$OPT}"   # extract long option argument (may be empty)
    OPTARG="${OPTARG#=}"      # if long option argument, remove assigning `=`
  fi
  case "$OPT" in
    a | alpha )    alpha=true ;;
    b | bravo )    needs_arg; bravo="$OPTARG" ;;
    c | charlie )  needs_arg; charlie="$OPTARG" ;;
    ??* )          die "Illegal option --$OPT" ;;  # bad long option
    ? )            exit 2 ;;  # bad short option (error reported via getopts)
  esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list

Gdy opcja jest dash (-), jest to opcja długa. getopts przetworzy rzeczywistą opcję long do $OPTARG, np. --bravo=foo oryginalnie ustawia OPT='-' i OPTARG='bravo=foo'. Strofa if ustawia $OPT na zawartość $OPTARG przed pierwszym znakiem równości (bravo w naszym przykładzie), a następnie usuwa ją z początku $OPTARG (dając =foo w tym kroku, lub pusty łańcuch, jeśli nie ma =). Na koniec usuwamy argument wiodący =. W tym momencie $OPT jest opcja krótka (jeden znak) lub długa (2 + znaki).

The case następnie dopasowuje krótkie lub długie opcje. W przypadku krótkich opcji {[2] } automatycznie narzeka na opcje i brakujące argumenty, więc musimy replikować je ręcznie za pomocą funkcji needs_arg, która kończy się, gdy $OPTARG jest pusta. Warunek ??* będzie pasował do pozostałych długich opcji (? pasuje do pojedynczego znaku i * pasuje do zera lub więcej, więc ??* pasuje do 2+ znaków), pozwalając nam na wydanie błędu "nielegalnej opcji" przed zakończeniem.

Mały błąd: jeśli ktoś poda nieprawidłową opcję long dla jednego znaku (i nie jest to również opcja krótka), zakończy się ona błędem, ale bez komunikatu. Implementacja ta zakłada bowiem, że była to krótka opcja. Można to śledzić za pomocą dodatkowej zmiennej w długiej opcji strofy poprzedzającej case, a następnie przetestować ją w ostatecznym stanie przypadku, ale uważam, że za dużo przypadku narożnego do kłopot.

(uwaga na temat nazw zmiennych z wielkimi literami: ogólnie rzecz biorąc, zaleca się zarezerwowanie zmiennych z wielkimi literami do użycia w systemie. Zachowuję $OPT jako wielkie litery, aby zachować zgodność z $OPTARG, ale to łamie tę konwencję. Myślę, że pasuje, ponieważ jest to coś, co system powinien zrobić, i powinno być bezpieczne, ponieważ nie ma standardów (afaik), które używają takiej zmiennej.)


Narzekać na nieoczekiwane argumenty do długich opcji, naśladować to, co zrobiliśmy dla argumentów obowiązkowych: użyj funkcji pomocniczej. Po prostu odwróć test, aby narzekać na argument, gdy nie oczekuje się go: {]}

no_arg() { if [ -n "$OPTARG" ]; then die "No arg allowed for --$OPT option"; fi; }

An starsza wersja tej odpowiedzi miała próbę zaakceptowania długich opcji z argumentami oddzielonymi spacjami, ale nie była wiarygodna; getopts mogła przedwcześnie zakończyć się przy założeniu, że argument był poza swoim zakresem i ręczne zwiększanie $OPTIND nie działa we wszystkich powłokach.

Można to osiągnąć za pomocą jednego z tych technik w zależności od powłoki:

A następnie zakończone czymś w rodzaju [ $# -gt $OPTIND ] && OPTIND=$((OPTIND+1))

 72
Author: Adam Katz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-27 20:45:42

Spójrz na shFlags która jest przenośną biblioteką powłoki (czyli: sh, bash, Dash, ksh, zsh na Linuksie, Solarisie itp.).

Sprawia, że dodawanie nowych flag jest tak proste, jak dodanie jednej linii do skryptu i zapewnia automatycznie wygenerowaną funkcję użytkowania.

Oto prosty Hello, world! za pomocą shFlag :

#!/bin/sh

# source shflags from current directory
. ./shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# say hello
echo "Hello, ${FLAGS_name}!"

Dla Systemów Operacyjnych, które mają rozszerzony getopt, który obsługuje długie opcje (np. Linux), możesz zrobić:

$ ./hello_world.sh --name Kate
Hello, Kate!

Do reszty, musisz użyć opcja krótka:

$ ./hello_world.sh -n Kate
Hello, Kate!

Dodanie nowego znacznika jest tak proste, jak dodanie nowego DEFINE_ call.

 34
Author: k0pernikus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-25 23:39:29

Używanie {[5] } z krótkimi / długimi opcjami i argumentami


Działa ze wszystkimi kombinacjami, np.:

  • foobar - F -- bar
  • foobar --foo-b
  • foobar-bf --bar --foobar
  • foobar-fbFBAshorty --bar-FB --arguments=longhorn
  • foobar-fA "text shorty" - B --arguments= "text longhorn"
  • bash foobar-F --barfoo
  • sh foobar-B --foobar - ...
  • bash ./ foobar-F --bar

Niektóre deklaracje dla tego przykładu

Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

Jak wyglądałaby funkcja Usage

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
  $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                  -f   --foo            Set foo to yes    ($bart)
                  -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

getops z długimi / krótkimi flagami oraz długimi argumentami

while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
             --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

Wyjście

##################################################################
echo "----------------------------------------------------------"
echo "RESULT short-foo      : $sfoo                                    long-foo      : $lfoo"
echo "RESULT short-bar      : $sbar                                    long-bar      : $lbar"
echo "RESULT short-foobar   : $sfoobar                                 long-foobar   : $lfoobar"
echo "RESULT short-barfoo   : $sbarfoo                                 long-barfoo   : $lbarfoo"
echo "RESULT short-arguments: $sarguments  with Argument = \"$sARG\"   long-arguments: $larguments and $lARG"

Połączenie powyższego w spójny skrypt

#!/bin/bash
# foobar: getopts with short and long options AND arguments

function _cleanup ()
{
  unset -f _usage _cleanup ; return 0
}

## Clear out nested functions on exit
trap _cleanup INT EXIT RETURN

###### some declarations for this example ######
Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
   $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                    -f   --foo            Set foo to yes    ($bart)
                      -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

##################################################################    
#######  "getopts" with: short options  AND  long options  #######
#######            AND  short/long arguments               #######
while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
               --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done
 34
Author: RapaNui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-23 12:25:31

W inny sposób...

# translate long options to short
for arg
do
    delim=""
    case "$arg" in
       --help) args="${args}-h ";;
       --verbose) args="${args}-v ";;
       --config) args="${args}-c ";;
       # pass through anything else
       *) [[ "${arg:0:1}" == "-" ]] || delim="\""
           args="${args}${delim}${arg}${delim} ";;
    esac
done
# reset the translated args
eval set -- $args
# now we can process with getopt
while getopts ":hvc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        c)  source $OPTARG ;;
        \?) usage ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage
        ;;
    esac
done
 24
Author: mtvee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-10 04:30:16

Rozwiązałem tak:

# A string with command options
options=$@

# An array with all the arguments
arguments=($options)

# Loop index
index=0

for argument in $options
  do
    # Incrementing index
    index=`expr $index + 1`

    # The conditions
    case $argument in
      -a) echo "key $argument value ${arguments[index]}" ;;
      -abc) echo "key $argument value ${arguments[index]}" ;;
    esac
  done

exit;
Czy ja jestem głupi czy co? getopt i getopts są takie mylące.
 18
Author: BenMorel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-21 10:11:22

Jeśli nie chcesz zależności getopt, możesz to zrobić:

while test $# -gt 0
do
  case $1 in

  # Normal option processing
    -h | --help)
      # usage and help
      ;;
    -v | --version)
      # version info
      ;;
  # ...

  # Special cases
    --)
      break
      ;;
    --*)
      # error unknown (long) option $1
      ;;
    -?)
      # error unknown (short) option $1
      ;;

  # FUN STUFF HERE:
  # Split apart combined short options
    -*)
      split=$1
      shift
      set -- $(echo "$split" | cut -c 2- | sed 's/./-& /g') "$@"
      continue
      ;;

  # Done with options
    *)
      break
      ;;
  esac

  # for testing purposes:
  echo "$1"

  shift
done

Oczywiście nie można używać długich opcji stylu z jednym myślnikiem. Jeśli chcesz dodać skrócone wersje (np. --verbos zamiast --verbose), musisz dodać je ręcznie.

Ale jeśli chcesz uzyskać funkcjonalność getopts wraz z długimi opcjami, jest to prosty sposób na to.

Umieściłem również ten fragment w gist.

 15
Author: jakesandlund,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-27 23:55:53

Wbudowany getopts nie może tego zrobić. Istnieje zewnętrzny getopt (1) program, który może to zrobić, ale można go uzyskać tylko na Linuksie z pakietu util-linux . Jest dostarczany z przykładowym skryptem getopt-parse.bash .

Istnieje również getopts_long napisany jako funkcja powłoki.

 11
Author: Nietzche-jou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-31 06:28:55
#!/bin/bash
while getopts "abc:d:" flag
do
  case $flag in
    a) echo "[getopts:$OPTIND]==> -$flag";;
    b) echo "[getopts:$OPTIND]==> -$flag";;
    c) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
    d) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
  esac
done

shift $((OPTIND-1))
echo "[otheropts]==> $@"

exit

.

#!/bin/bash
until [ -z "$1" ]; do
  case $1 in
    "--dlong")
      shift
      if [ "${1:1:0}" != "-" ]
      then
        echo "==> dlong $1"
        shift
      fi;;
    *) echo "==> other $1"; shift;;
  esac
done
exit
 9
Author: 3ED,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-09 14:54:29

W ksh93, getopts obsługuje długie nazwy...

while getopts "f(file):s(server):" flag
do
    echo "$flag" $OPTIND $OPTARG
done

Albo tak samo samouczki, które znalazłem, mówiły. Spróbuj i zobacz.

 7
Author: Richard Lynch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-12 07:46:44

Piszę tylko skrypty powłoki od czasu do czasu i wypadają z praktyki, więc wszelkie opinie są mile widziane.

Używając strategii zaproponowanej przez @ Arvid Requate, zauważyliśmy pewne błędy użytkownika. Użytkownik, który zapomni dodać wartość, niechcący potraktuje nazwę następnej opcji jako wartość:

./getopts_test.sh --loglevel= --toc=TRUE

Spowoduje, że wartość "loglevel" będzie postrzegana jako "--Toc = TRUE". To może należy unikać.

Zaadaptowałem kilka pomysłów na sprawdzenie błędu użytkownika dla CLI z http://mwiki.wooledge.org/BashFAQ/035 dyskusja o ręcznym parsowaniu. Wstawiłem sprawdzanie błędów do obsługi argumentów " - "i"--".

Potem zacząłem grzebać w składni, więc wszelkie błędy tutaj są wyłącznie moją winą, a nie oryginalnymi autorami.

Moje podejście pomaga użytkownikom, którzy wolą wchodzić długo ze znakiem równości lub bez niego. Oznacza to, że powinien mieć taką samą odpowiedź na "--loglevel 9 " jak "--loglevel=9". W metodzie -- / space nie jest możliwe poznanie na pewno, jeśli użytkownik zapomni argumentu, więc pewne zgadywanie jest potrzebne.

  1. jeśli użytkownik ma format znaku long/equal (--opt=), to spacja po = powoduje błąd, ponieważ argument nie został podany.
  2. jeśli użytkownik ma argumenty long/space (--opt ), skrypt ten powoduje błąd, jeśli żaden argument nie następuje (koniec polecenia) lub jeśli argument zaczyna się dash)

Jeśli zaczynasz od tego, istnieje interesująca różnica między "--opt = wartość " a formaty "--opt value". Ze znakiem równości argument linii poleceń jest postrzegany jako "opt = value", a praca do obsługi, którą jest parsowanie łańcuchów, aby oddzielić przy"=". W przeciwieństwie do "--opt value", nazwa argumentu to" opt " i mamy wyzwanie uzyskania następnej wartości podanej w wierszu poleceń. @Arvid Requate użył ${!OPTIND}, pośrednie odniesienie. Nadal tego nie rozumiem, cóż, w ogóle, a komentarze w BashFAQ wydają się ostrzegać przed tym stylem ( http://mywiki.wooledge.org/BashFAQ/006 ). BTW, nie wydaje mi się, aby poprzednie komentarze na temat znaczenia OPTIND=$(( $OPTIND + 1 )) były poprawne. Nie widzę nic złego w pominięciu tego.

W najnowszej wersji tego skryptu znacznik-v oznacza szczegółowy Wydruk.

Zapisz go w pliku o nazwie "cli-5.sh", wykonywalne, i każdy z nich będzie działać, lub nie w pożądany sposób

./cli-5.sh  -v --loglevel=44 --toc  TRUE
./cli-5.sh  -v --loglevel=44 --toc=TRUE
./cli-5.sh --loglevel 7
./cli-5.sh --loglevel=8
./cli-5.sh -l9

./cli-5.sh  --toc FALSE --loglevel=77
./cli-5.sh  --toc=FALSE --loglevel=77

./cli-5.sh   -l99 -t yyy
./cli-5.sh   -l 99 -t yyy

Oto Przykładowe wyjście sprawdzania błędów u użytkownika intpu

$ ./cli-5.sh  --toc --loglevel=77
ERROR: toc value must not have dash at beginning
$ ./cli-5.sh  --toc= --loglevel=77
ERROR: value for toc undefined

Powinieneś rozważyć włączenie opcji - v, ponieważ wyświetla wewnętrzne opcje OPTIND i OPTTARG

#/usr/bin/env bash

## Paul Johnson
## 20171016
##

## Combines ideas from
## https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
## by @Arvid Requate, and http://mwiki.wooledge.org/BashFAQ/035

# What I don't understand yet: 
# In @Arvid REquate's answer, we have 
# val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
# this works, but I don't understand it!


die() {
    printf '%s\n' "$1" >&2
    exit 1
}

printparse(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
    fi
}

showme(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'VERBOSE: %s\n' "$1" >&2;
    fi
}


VERBOSE=0
loglevel=0
toc="TRUE"

optspec=":vhl:t:-:"
while getopts "$optspec" OPTCHAR; do

    showme "OPTARG:  ${OPTARG[*]}"
    showme "OPTIND:  ${OPTIND[*]}"
    case "${OPTCHAR}" in
        -)
            case "${OPTARG}" in
                loglevel) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) # CAUTION! no effect?
                    printparse "--${OPTARG}" "  " "${val}"
                    loglevel="${val}"
                    shift
                    ;;
                loglevel=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        printparse "--${opt}" "=" "${val}"
                        loglevel="${val}"
                        ## shift CAUTION don't shift this, fails othewise
                    else
                        die "ERROR: $opt value must be supplied"
                    fi
                    ;;
                toc) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) #??
                    printparse "--${opt}" " " "${val}"
                    toc="${val}"
                    shift
                    ;;
                toc=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        toc=${val}
                        printparse "--$opt" " -> " "$toc"
                        ##shift ## NO! dont shift this
                    else
                        die "ERROR: value for $opt undefined"
                    fi
                    ;;

                help)
                    echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
                    exit 2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h|-\?|--help)
            ## must rewrite this for all of the arguments
            echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
            exit 2
            ;;
        l)
            loglevel=${OPTARG}
            printparse "-l" " "  "${loglevel}"
            ;;
        t)
            toc=${OPTARG}
            ;;
        v)
            VERBOSE=1
            ;;

        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done



echo "
After Parsing values
"
echo "loglevel  $loglevel" 
echo "toc  $toc"
 6
Author: pauljohn32,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-17 15:00:43

Wynalezienie jeszcze jednej wersji koła...

Ta funkcja jest (miejmy nadzieję) zgodnym z POSIX zwykłym zamiennikiem powłoki bourne ' a dla GNU getopt. Obsługuje krótkie / długie opcje, które mogą akceptować obowiązkowe / opcjonalne / bez argumentów, a sposób, w jaki opcje są określone, jest prawie identyczny z GNU getopt, więc konwersja jest trywialna.

Oczywiście jest to wciąż spory kawałek kodu do wrzucenia do skryptu, ale jest to około połowa linii znanej powłoki getopt_long funkcji i może być korzystniejsza w przypadkach, gdy chcemy zastąpić istniejące zastosowania GNU getopt.

Jest to całkiem nowy kod, więc YMMV (i zdecydowanie proszę dać mi znać, jeśli nie jest to zgodne z POSIX z jakiegokolwiek powodu - przenośność była intencją od samego początku, ale nie mam użytecznego środowiska testowego POSIX).

Kod i przykład użycia:

#!/bin/sh
# posix_getopt shell function
# Author: Phil S.
# Version: 1.0
# Created: 2016-07-05
# URL: http://stackoverflow.com/a/37087374/324105

# POSIX-compatible argument quoting and parameter save/restore
# http://www.etalabs.net/sh_tricks.html
# Usage:
# parameters=$(save "$@") # save the original parameters.
# eval "set -- ${parameters}" # restore the saved parameters.
save () {
    local param
    for param; do
        printf %s\\n "$param" \
            | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
    done
    printf %s\\n " "
}

# Exit with status $1 after displaying error message $2.
exiterr () {
    printf %s\\n "$2" >&2
    exit $1
}

# POSIX-compatible command line option parsing.
# This function supports long options and optional arguments, and is
# a (largely-compatible) drop-in replacement for GNU getopt.
#
# Instead of:
# opts=$(getopt -o "$shortopts" -l "$longopts" -- "$@")
# eval set -- ${opts}
#
# We instead use:
# opts=$(posix_getopt "$shortopts" "$longopts" "$@")
# eval "set -- ${opts}"
posix_getopt () { # args: "$shortopts" "$longopts" "$@"
    local shortopts longopts \
          arg argtype getopt nonopt opt optchar optword suffix

    shortopts="$1"
    longopts="$2"
    shift 2

    getopt=
    nonopt=
    while [ $# -gt 0 ]; do
        opt=
        arg=
        argtype=
        case "$1" in
            # '--' means don't parse the remaining options
            ( -- ) {
                getopt="${getopt}$(save "$@")"
                shift $#
                break
            };;
            # process short option
            ( -[!-]* ) {         # -x[foo]
                suffix=${1#-?}   # foo
                opt=${1%$suffix} # -x
                optchar=${opt#-} # x
                case "${shortopts}" in
                    ( *${optchar}::* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *${optchar}:* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 "$1 requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 "$1 requires an argument";;
                            esac
                        fi
                    };;
                    ( *${optchar}* ) { # no argument
                        argtype=none
                        arg=
                        shift
                        # Handle multiple no-argument parameters combined as
                        # -xyz instead of -x -y -z. If we have just shifted
                        # parameter -xyz, we now replace it with -yz (which
                        # will be processed in the next iteration).
                        if [ -n "${suffix}" ]; then
                            eval "set -- $(save "-${suffix}")$(save "$@")"
                        fi
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # process long option
            ( --?* ) {            # --xarg[=foo]
                suffix=${1#*=}    # foo (unless there was no =)
                if [ "${suffix}" = "$1" ]; then
                    suffix=
                fi
                opt=${1%=$suffix} # --xarg
                optword=${opt#--} # xarg
                case ",${longopts}," in
                    ( *,${optword}::,* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *,${optword}:,* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 \
                                       "--${optword} requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 \
                                       "--${optword} requires an argument";;
                            esac
                        fi
                    };;
                    ( *,${optword},* ) { # no argument
                        if [ -n "${suffix}" ]; then
                            exiterr 1 "--${optword} does not take an argument"
                        fi
                        argtype=none
                        arg=
                        shift
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # any other parameters starting with -
            ( -* ) exiterr 1 "Unknown option $1";;
            # remember non-option parameters
            ( * ) nonopt="${nonopt}$(save "$1")"; shift;;
        esac

        if [ -n "${opt}" ]; then
            getopt="${getopt}$(save "$opt")"
            case "${argtype}" in
                ( optional|required ) {
                    getopt="${getopt}$(save "$arg")"
                };;
            esac
        fi
    done

    # Generate function output, suitable for:
    # eval "set -- $(posix_getopt ...)"
    printf %s "${getopt}"
    if [ -n "${nonopt}" ]; then
        printf %s "$(save "--")${nonopt}"
    fi
}

Przykładowe użycie:

# Process command line options
shortopts="hvd:c::s::L:D"
longopts="help,version,directory:,client::,server::,load:,delete"
#opts=$(getopt -o "$shortopts" -l "$longopts" -n "$(basename $0)" -- "$@")
opts=$(posix_getopt "$shortopts" "$longopts" "$@")
if [ $? -eq 0 ]; then
    #eval set -- ${opts}
    eval "set -- ${opts}"
    while [ $# -gt 0 ]; do
        case "$1" in
            ( --                ) shift; break;;
            ( -h|--help         ) help=1; shift; break;;
            ( -v|--version      ) version_help=1; shift; break;;
            ( -d|--directory    ) dir=$2; shift 2;;
            ( -c|--client       ) useclient=1; client=$2; shift 2;;
            ( -s|--server       ) startserver=1; server_name=$2; shift 2;;
            ( -L|--load         ) load=$2; shift 2;;
            ( -D|--delete       ) delete=1; shift;;
        esac
    done
else
    shorthelp=1 # getopt returned (and reported) an error.
fi
 5
Author: phils,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-07 10:27:57

Przyjęta odpowiedź bardzo ładnie wskazuje na wszystkie niedociągnięcia wbudowanego Basha getopts. Odpowiedź kończy się na:

Więc chociaż możliwe jest napisanie większej ilości kodu, aby obejść brak wsparcia dla długich opcji, jest to o wiele więcej pracy i częściowo zaprzecza celowi użycia parsera getopt w celu uproszczenia kodu.

I mimo, że zasadniczo zgadzam się z tym stwierdzeniem, mam wrażenie, że wiele razy implementowaliśmy tę funkcję w różne skrypty uzasadniają włożenie trochę wysiłku w stworzenie "znormalizowanego", dobrze przetestowanego rozwiązania.

Jako taki "uaktualniłem" Basha wbudowanego w getopts poprzez implementację getopts_long w czystym bash, bez zewnętrznych zależności. Użycie funkcji jest w 100% zgodne z wbudowanym getopts.

Poprzez włączenie getopts_long (które jest hostowane na GitHub ) do skryptu, odpowiedź na oryginalne pytanie może być zaimplementowana w prosty sposób:

source "${PATH_TO}/getopts_long.bash"

while getopts_long ':c: copyfile:' OPTKEY; do
    case ${OPTKEY} in
        'c'|'copyfile')
            echo 'file supplied -- ${OPTARG}'
            ;;
        '?')
            echo "INVALID OPTION -- ${OPTARG}" >&2
            exit 1
            ;;
        ':')
            echo "MISSING ARGUMENT for option -- ${OPTARG}" >&2
            exit 1
            ;;
        *)
            echo "Misconfigured OPTSPEC or uncaught option -- ${OPTKEY}" >&2
            exit 1
            ;;
    esac
done

shift $(( OPTIND - 1 ))
[[ "${1}" == "--" ]] && shift
 5
Author: UrsaDK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-16 07:49:30

Nie mam jeszcze wystarczająco rep, aby skomentować lub zagłosować na jego rozwiązanie, ale odpowiedź sme ' a zadziałała bardzo dobrze dla mnie. Jedynym problemem, na który wpadłem było to, że argumenty kończą się owinięte w pojedyncze cudzysłowy(więc mam je rozebrać).

Dodałem również przykładowe zastosowania i tekst pomocy. Dodam tutaj moją nieco rozszerzoną wersję:

#!/bin/bash

# getopt example
# from: https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
HELP_TEXT=\
"   USAGE:\n
    Accepts - and -- flags, can specify options that require a value, and can be in any order. A double-hyphen (--) will stop processing options.\n\n

    Accepts the following forms:\n\n

    getopt-example.sh -a -b -c value-for-c some-arg\n
    getopt-example.sh -c value-for-c -a -b some-arg\n
    getopt-example.sh -abc some-arg\n
    getopt-example.sh --along --blong --clong value-for-c -a -b -c some-arg\n
    getopt-example.sh some-arg --clong value-for-c\n
    getopt-example.sh
"

aflag=false
bflag=false
cargument=""

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc:h\? -l along,blong,help,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag=true ;;
    -b|--blong) bflag=true ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    -h|--help|-\?) echo -e $HELP_TEXT; exit;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done

# to remove the single quotes around arguments, pipe the output into:
# | sed -e "s/^'\\|'$//g"  (just leading/trailing) or | sed -e "s/'//g"  (all)

echo aflag=${aflag}
echo bflag=${bflag}
echo cargument=${cargument}

while [ $# -gt 0 ]
do
    echo arg=$1
    shift

    if [[ $aflag == true ]]; then
        echo a is true
    fi

done
 3
Author: kghastie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45

Tutaj możesz znaleźć kilka różnych podejść do złożonego parsowania opcji w bash: http://mywiki.wooledge.org/ComplexOptionParsing

Stworzyłem następujący i myślę, że jest dobry, ponieważ jest to minimalny kod i zarówno długie, jak i krótkie opcje działają. Długa opcja może również mieć wiele argumentów w tym podejściu.

#!/bin/bash
# Uses bash extensions.  Not portable as written.

declare -A longoptspec
longoptspec=( [loglevel]=1 ) #use associative array to declare how many arguments a long option expects, in this case we declare that loglevel expects/has one argument, long options that aren't listed i n this way will have zero arguments by default
optspec=":h-:"
while getopts "$optspec" opt; do
while true; do
    case "${opt}" in
        -) #OPTARG is name-of-long-option or name-of-long-option=value
            if [[ "${OPTARG}" =~ .*=.* ]] #with this --key=value format only one argument is possible
            then
                opt=${OPTARG/=*/}
                OPTARG=${OPTARG#*=}
                ((OPTIND--))    
            else #with this --key value1 value2 format multiple arguments are possible
                opt="$OPTARG"
                OPTARG=(${@:OPTIND:$((longoptspec[$opt]))})
            fi
            ((OPTIND+=longoptspec[$opt]))
            continue #now that opt/OPTARG are set we can process them as if getopts would've given us long options
            ;;
        loglevel)
          loglevel=$OPTARG
            ;;
        h|help)
            echo "usage: $0 [--loglevel[=]<value>]" >&2
            exit 2
            ;;
    esac
break; done
done

# End of file
 3
Author: user3573558,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-01 19:25:55

Pracuję nad tym tematem od dłuższego czasu... i stworzyłem własną bibliotekę, którą będziesz potrzebował do źródła w swoim skrypcie głównym. Zobacz libopt4shell i cd2mpc dla przykładu. Mam nadzieję, że to pomoże !

 2
Author: liealgebra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-11 10:59:42

Ulepszone rozwiązanie:

# translate long options to short
# Note: This enable long options but disable "--?*" in $OPTARG, or disable long options after  "--" in option fields.
for ((i=1;$#;i++)) ; do
    case "$1" in
        --)
            # [ ${args[$((i-1))]} == ... ] || EndOpt=1 ;;& # DIRTY: we still can handle some execptions...
            EndOpt=1 ;;&
        --version) ((EndOpt)) && args[$i]="$1"  || args[$i]="-V";;
        # default case : short option use the first char of the long option:
        --?*) ((EndOpt)) && args[$i]="$1"  || args[$i]="-${1:2:1}";;
        # pass through anything else:
        *) args[$i]="$1" ;;
    esac
    shift
done
# reset the translated args
set -- "${args[@]}"

function usage {
echo "Usage: $0 [options] files" >&2
    exit $1
}

# now we can process with getopt
while getopts ":hvVc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        V)  echo $Version ; exit ;;
        c)  source $OPTARG ;;
        \?) echo "unrecognized option: -$opt" ; usage -1 ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage -1
        ;;
    esac
done

shift $((OPTIND-1))
[[ "$1" == "--" ]] && shift
 2
Author: jbar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-16 10:37:36

Być może prostsze jest użycie ksh, tylko dla części getopts, jeśli potrzebne są długie opcje wiersza poleceń, ponieważ tam można to łatwiej zrobić.

# Working Getopts Long => KSH

#! /bin/ksh
# Getopts Long
USAGE="s(showconfig)"
USAGE+="c:(createdb)"
USAGE+="l:(createlistener)"
USAGE+="g:(generatescripts)"
USAGE+="r:(removedb)"
USAGE+="x:(removelistener)"
USAGE+="t:(createtemplate)"
USAGE+="h(help)"

while getopts "$USAGE" optchar ; do
    case $optchar in
    s)  echo "Displaying Configuration" ;;
        c)  echo "Creating Database $OPTARG" ;;
    l)  echo "Creating Listener LISTENER_$OPTARG" ;;
    g)  echo "Generating Scripts for Database $OPTARG" ;;
    r)  echo "Removing Database $OPTARG" ;;
    x)  echo "Removing Listener LISTENER_$OPTARG" ;;
    t)  echo "Creating Database Template" ;;
    h)  echo "Help" ;;
    esac
done
 2
Author: efstathiou_e,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-26 14:49:28

Chciałem czegoś bez zewnętrznych zależności, ze ścisłą obsługą bash (-u) i potrzebowałem tego do pracy nawet na starszych wersjach bash. Obsługuje różne rodzaje param:

  • krótkie Boole (- h)
  • krótkie opcje (- i " obraz.jpg")
  • long bools (--help)
  • równa się options (--file= " nazwa pliku.ext")
  • opcje spacji (--file " nazwa pliku.ext")
  • concatinated bools (- hvm)

Po prostu wstaw na górze swojego skrypt:

# Check if a list of params contains a specific param
# usage: if _param_variant "h|?|help p|path f|file long-thing t|test-thing" "file" ; then ...
# the global variable $key is updated to the long notation (last entry in the pipe delineated list, if applicable)
_param_variant() {
  for param in $1 ; do
    local variants=${param//\|/ }
    for variant in $variants ; do
      if [[ "$variant" = "$2" ]] ; then
        # Update the key to match the long version
        local arr=(${param//\|/ })
        let last=${#arr[@]}-1
        key="${arr[$last]}"
        return 0
      fi
    done
  done
  return 1
}

# Get input parameters in short or long notation, with no dependencies beyond bash
# usage:
#     # First, set your defaults
#     param_help=false
#     param_path="."
#     param_file=false
#     param_image=false
#     param_image_lossy=true
#     # Define allowed parameters
#     allowed_params="h|?|help p|path f|file i|image image-lossy"
#     # Get parameters from the arguments provided
#     _get_params $*
#
# Parameters will be converted into safe variable names like:
#     param_help,
#     param_path,
#     param_file,
#     param_image,
#     param_image_lossy
#
# Parameters without a value like "-h" or "--help" will be treated as
# boolean, and will be set as param_help=true
#
# Parameters can accept values in the various typical ways:
#     -i "path/goes/here"
#     --image "path/goes/here"
#     --image="path/goes/here"
#     --image=path/goes/here
# These would all result in effectively the same thing:
#     param_image="path/goes/here"
#
# Concatinated short parameters (boolean) are also supported
#     -vhm is the same as -v -h -m
_get_params(){

  local param_pair
  local key
  local value
  local shift_count

  while : ; do
    # Ensure we have a valid param. Allows this to work even in -u mode.
    if [[ $# == 0 || -z $1 ]] ; then
      break
    fi

    # Split the argument if it contains "="
    param_pair=(${1//=/ })
    # Remove preceeding dashes
    key="${param_pair[0]#--}"

    # Check for concatinated boolean short parameters.
    local nodash="${key#-}"
    local breakout=false
    if [[ "$nodash" != "$key" && ${#nodash} -gt 1 ]]; then
      # Extrapolate multiple boolean keys in single dash notation. ie. "-vmh" should translate to: "-v -m -h"
      local short_param_count=${#nodash}
      let new_arg_count=$#+$short_param_count-1
      local new_args=""
      # $str_pos is the current position in the short param string $nodash
      for (( str_pos=0; str_pos<new_arg_count; str_pos++ )); do
        # The first character becomes the current key
        if [ $str_pos -eq 0 ] ; then
          key="${nodash:$str_pos:1}"
          breakout=true
        fi
        # $arg_pos is the current position in the constructed arguments list
        let arg_pos=$str_pos+1
        if [ $arg_pos -gt $short_param_count ] ; then
          # handle other arguments
          let orignal_arg_number=$arg_pos-$short_param_count+1
          local new_arg="${!orignal_arg_number}"
        else
          # break out our one argument into new ones
          local new_arg="-${nodash:$str_pos:1}"
        fi
        new_args="$new_args \"$new_arg\""
      done
      # remove the preceding space and set the new arguments
      eval set -- "${new_args# }"
    fi
    if ! $breakout ; then
      key="$nodash"
    fi

    # By default we expect to shift one argument at a time
    shift_count=1
    if [ "${#param_pair[@]}" -gt "1" ] ; then
      # This is a param with equals notation
      value="${param_pair[1]}"
    else
      # This is either a boolean param and there is no value,
      # or the value is the next command line argument
      # Assume the value is a boolean true, unless the next argument is found to be a value.
      value=true
      if [[ $# -gt 1 && -n "$2" ]]; then
        local nodash="${2#-}"
        if [ "$nodash" = "$2" ]; then
          # The next argument has NO preceding dash so it is a value
          value="$2"
          shift_count=2
        fi
      fi
    fi

    # Check that the param being passed is one of the allowed params
    if _param_variant "$allowed_params" "$key" ; then
      # --key-name will now become param_key_name
      eval param_${key//-/_}="$value"
    else
      printf 'WARNING: Unknown option (ignored): %s\n' "$1" >&2
    fi
    shift $shift_count
  done
}

I używaj go tak:

# Assign defaults for parameters
param_help=false
param_path=$(pwd)
param_file=false
param_image=true
param_image_lossy=true
param_image_lossy_quality=85

# Define the params we will allow
allowed_params="h|?|help p|path f|file i|image image-lossy image-lossy-quality"

# Get the params from arguments provided
_get_params $*
 2
Author: Heath Dutton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 18:57:31

Getopts "może być używany" do parsowania długich opcji, o ile nie oczekujesz, że będą miały argumenty...

Oto jak:

$ cat > longopt
while getopts 'e:-:' OPT; do
  case $OPT in
    e) echo echo: $OPTARG;;
    -) #long option
       case $OPTARG in
         long-option) echo long option;;
         *) echo long option: $OPTARG;;
       esac;;
  esac
done

$ bash longopt -e asd --long-option --long1 --long2 -e test
echo: asd
long option
long option: long1
long option: long2
echo: test

Jeśli spróbujesz użyć OPTIND do uzyskania parametru dla opcji long, getopts potraktuje go jako pierwszy bez opcjonalnego parametru pozycyjnego i przestanie analizować inne parametry. W takim przypadku lepiej będzie obsługiwać go ręcznie za pomocą prostego polecenia case.

To "zawsze" zadziała:

$ cat >longopt2
while (($#)); do
    OPT=$1
    shift
    case $OPT in
        --*) case ${OPT:2} in
            long1) echo long1 option;;
            complex) echo comples with argument $1; shift;;
        esac;;

        -*) case ${OPT:1} in
            a) echo short option a;;
            b) echo short option b with parameter $1; shift;;
        esac;;
    esac
done


$ bash longopt2 --complex abc -a --long -b test
comples with argument abc
short option a
short option b with parameter test
/ Align = "left" / elastyczny jak getopts i musisz zrobić wiele z kodu sprawdzania błędów samodzielnie w instancjach przypadku... Ale jest to opcja.
 1
Author: estani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-01 14:13:09

Aby zachować kompatybilność między platformami i uniknąć polegania na zewnętrznych plikach wykonywalnych, przeportowałem kod z innego języka.

Uważam, że jest bardzo łatwy w użyciu, oto przykład:

ArgParser::addArg "[h]elp"    false    "This list"
ArgParser::addArg "[q]uiet"   false    "Supress output"
ArgParser::addArg "[s]leep"   1        "Seconds to sleep"
ArgParser::addArg "v"         1        "Verbose mode"

ArgParser::parse "$@"

ArgParser::isset help && ArgParser::showArgs

ArgParser::isset "quiet" \
   && echo "Quiet!" \
   || echo "Noisy!"

local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
   && echo "Sleep for $__sleep seconds" \
   || echo "No value passed for sleep"

# This way is often more convienient, but is a little slower
echo "Sleep set to: $( ArgParser::getArg sleep )"

Wymagany BASH jest nieco dłuższy niż mógłby być, ale chciałem uniknąć polegania na tablicach asocjacyjnych Basha 4. Możesz również pobrać to bezpośrednio z http://nt4.com/bash/argparser.inc.sh

#!/usr/bin/env bash

# Updates to this script may be found at
# http://nt4.com/bash/argparser.inc.sh

# Example of runtime usage:
# mnc.sh --nc -q Caprica.S0*mkv *.avi *.mp3 --more-options here --host centos8.host.com

# Example of use in script (see bottom)
# Just include this file in yours, or use
# source argparser.inc.sh

unset EXPLODED
declare -a EXPLODED
function explode 
{
    local c=$# 
    (( c < 2 )) && 
    {
        echo function "$0" is missing parameters 
        return 1
    }

    local delimiter="$1"
    local string="$2"
    local limit=${3-99}

    local tmp_delim=$'\x07'
    local delin=${string//$delimiter/$tmp_delim}
    local oldifs="$IFS"

    IFS="$tmp_delim"
    EXPLODED=($delin)
    IFS="$oldifs"
}


# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
# Usage: local "$1" && upvar $1 "value(s)"
upvar() {
    if unset -v "$1"; then           # Unset & validate varname
        if (( $# == 2 )); then
            eval $1=\"\$2\"          # Return single value
        else
            eval $1=\(\"\${@:2}\"\)  # Return array
        fi
    fi
}

function decho
{
    :
}

function ArgParser::check
{
    __args=${#__argparser__arglist[@]}
    for (( i=0; i<__args; i++ ))
    do
        matched=0
        explode "|" "${__argparser__arglist[$i]}"
        if [ "${#1}" -eq 1 ]
        then
            if [ "${1}" == "${EXPLODED[0]}" ]
            then
                decho "Matched $1 with ${EXPLODED[0]}"
                matched=1

                break
            fi
        else
            if [ "${1}" == "${EXPLODED[1]}" ]
            then
                decho "Matched $1 with ${EXPLODED[1]}"
                matched=1

                break
            fi
        fi
    done
    (( matched == 0 )) && return 2
    # decho "Key $key has default argument of ${EXPLODED[3]}"
    if [ "${EXPLODED[3]}" == "false" ]
    then
        return 0
    else
        return 1
    fi
}

function ArgParser::set
{
    key=$3
    value="${1:-true}"
    declare -g __argpassed__$key="$value"
}

function ArgParser::parse
{

    unset __argparser__argv
    __argparser__argv=()
    # echo parsing: "$@"

    while [ -n "$1" ]
    do
        # echo "Processing $1"
        if [ "${1:0:2}" == '--' ]
        then
            key=${1:2}
            value=$2
        elif [ "${1:0:1}" == '-' ]
        then
            key=${1:1}               # Strip off leading -
            value=$2
        else
            decho "Not argument or option: '$1'" >& 2
            __argparser__argv+=( "$1" )
            shift
            continue
        fi
        # parameter=${tmp%%=*}     # Extract name.
        # value=${tmp##*=}         # Extract value.
        decho "Key: '$key', value: '$value'"
        # eval $parameter=$value
        ArgParser::check $key
        el=$?
        # echo "Check returned $el for $key"
        [ $el -eq  2 ] && decho "No match for option '$1'" >&2 # && __argparser__argv+=( "$1" )
        [ $el -eq  0 ] && decho "Matched option '${EXPLODED[2]}' with no arguments"        >&2 && ArgParser::set true "${EXPLODED[@]}"
        [ $el -eq  1 ] && decho "Matched option '${EXPLODED[2]}' with an argument of '$2'"   >&2 && ArgParser::set "$2" "${EXPLODED[@]}" && shift
        shift
    done
}

function ArgParser::isset
{
    declare -p "__argpassed__$1" > /dev/null 2>&1 && return 0
    return 1
}

function ArgParser::getArg
{
    # This one would be a bit silly, since we can only return non-integer arguments ineffeciently
    varname="__argpassed__$1"
    echo "${!varname}"
}

##
# usage: tryAndGetArg <argname> into <varname>
# returns: 0 on success, 1 on failure
function ArgParser::tryAndGetArg
{
    local __varname="__argpassed__$1"
    local __value="${!__varname}"
    test -z "$__value" && return 1
    local "$3" && upvar $3 "$__value"
    return 0
}

function ArgParser::__construct
{
    unset __argparser__arglist
    # declare -a __argparser__arglist
}

##
# @brief add command line argument
# @param 1 short and/or long, eg: [s]hort
# @param 2 default value
# @param 3 description
##
function ArgParser::addArg
{
    # check for short arg within long arg
    if [[ "$1" =~ \[(.)\] ]]
    then
        short=${BASH_REMATCH[1]}
        long=${1/\[$short\]/$short}
    else
        long=$1
    fi
    if [ "${#long}" -eq 1 ]
    then
        short=$long
        long=''
    fi
    decho short: "$short"
    decho long: "$long"
    __argparser__arglist+=("$short|$long|$1|$2|$3")
}

## 
# @brief show available command line arguments
##
function ArgParser::showArgs
{
    # declare -p | grep argparser
    printf "Usage: %s [OPTION...]\n\n" "$( basename "${BASH_SOURCE[0]}" )"
    printf "Defaults for the options are specified in brackets.\n\n";

    __args=${#__argparser__arglist[@]}
    for (( i=0; i<__args; i++ ))
    do
        local shortname=
        local fullname=
        local default=
        local description=
        local comma=

        explode "|" "${__argparser__arglist[$i]}"

        shortname="${EXPLODED[0]:+-${EXPLODED[0]}}" # String Substitution Guide: 
        fullname="${EXPLODED[1]:+--${EXPLODED[1]}}" # http://tldp.org/LDP/abs/html/parameter-substitution.html
        test -n "$shortname" \
            && test -n "$fullname" \
            && comma=","

        default="${EXPLODED[3]}"
        case $default in
            false )
                default=
                ;;
            "" )
                default=
                ;;
            * )
                default="[$default]"
        esac

        description="${EXPLODED[4]}"

        printf "  %2s%1s %-19s %s %s\n" "$shortname" "$comma" "$fullname" "$description" "$default"
    done
}

function ArgParser::test
{
    # Arguments with a default of 'false' do not take paramaters (note: default
    # values are not applied in this release)

    ArgParser::addArg "[h]elp"      false       "This list"
    ArgParser::addArg "[q]uiet" false       "Supress output"
    ArgParser::addArg "[s]leep" 1           "Seconds to sleep"
    ArgParser::addArg "v"           1           "Verbose mode"

    ArgParser::parse "$@"

    ArgParser::isset help && ArgParser::showArgs

    ArgParser::isset "quiet" \
        && echo "Quiet!" \
        || echo "Noisy!"

    local __sleep
    ArgParser::tryAndGetArg sleep into __sleep \
        && echo "Sleep for $__sleep seconds" \
        || echo "No value passed for sleep"

    # This way is often more convienient, but is a little slower
    echo "Sleep set to: $( ArgParser::getArg sleep )"

    echo "Remaining command line: ${__argparser__argv[@]}"

}

if [ "$( basename "$0" )" == "argparser.inc.sh" ]
then
    ArgParser::test "$@"
fi
 1
Author: Orwellophile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-16 10:02:31

Jeśli wszystkie długie opcje mają unikalne i pasujące pierwsze znaki jako krótkie opcje, na przykład

./slamm --chaos 23 --plenty test -quiet

Jest tym samym co

./slamm -c 23 -p test -q

Możesz użyć tego przed getopts, aby przepisać $args:

# change long options to short options

for arg; do 
    [[ "${arg:0:1}" == "-" ]] && delim="" || delim="\""
    if [ "${arg:0:2}" == "--" ]; 
       then args="${args} -${arg:2:1}" 
       else args="${args} ${delim}${arg}${delim}"
    fi
done

# reset the incoming args
eval set -- $args

# proceed as usual
while getopts ":b:la:h" OPTION; do
    .....

Dzięki za mtvee za inspirację; -)

 1
Author: commonpike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-21 10:11:58

Jeśli tak chcesz wywołać skrypt

myscript.sh --input1 "ABC" --input2 "PQR" --input2 "XYZ"

Następnie możesz wykonać ten najprostszy sposób, aby to osiągnąć za pomocą getopt i -- longoptions

Spróbuj tego, mam nadzieję, że jest to przydatne

# Read command line options
ARGUMENT_LIST=(
    "input1"
    "input2"
    "input3"
)



# read arguments
opts=$(getopt \
    --longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")" \
    --name "$(basename "$0")" \
    --options "" \
    -- "$@"
)


echo $opts

eval set --$opts

while true; do
    case "$1" in
    --input1)  
        shift
        empId=$1
        ;;
    --input2)  
        shift
        fromDate=$1
        ;;
    --input3)  
        shift
        toDate=$1
        ;;
      --)
        shift
        break
        ;;
    esac
    shift
done
 1
Author: Ashish Shetkar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-02 09:55:18

Builtin getopts analizuj tylko krótkie opcje (z wyjątkiem ksh93), ale nadal możesz dodać kilka linii skryptów, aby getopts obsługiwał długie opcje.

Oto część kodu znaleziona w http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts

  #== set short options ==#
SCRIPT_OPTS=':fbF:B:-:h'
  #== set long options associated with short one ==#
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
    [foo]=f
    [bar]=b
    [foobar]=F
    [barfoo]=B
    [help]=h
    [man]=h
)

  #== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
    #== translate long options to short ==#
    if [[ "x$OPTION" == "x-" ]]; then
        LONG_OPTION=$OPTARG
        LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2)
        LONG_OPTIND=-1
        [[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1)
        [[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="\$$LONG_OPTIND"
        OPTION=${ARRAY_OPTS[$LONG_OPTION]}
        [[ "x$OPTION" = "x" ]] &&  OPTION="?" OPTARG="-$LONG_OPTION"

        if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
            if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then 
                OPTION=":" OPTARG="-$LONG_OPTION"
            else
                OPTARG="$LONG_OPTARG";
                if [[ $LONG_OPTIND -ne -1 ]]; then
                    [[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
                    shift $OPTIND
                    OPTIND=1
                fi
            fi
        fi
    fi

    #== options follow by another option instead of argument ==#
    if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then 
        OPTARG="$OPTION" OPTION=":"
    fi

    #== manage options ==#
    case "$OPTION" in
        f  ) foo=1 bar=0                    ;;
        b  ) foo=0 bar=1                    ;;
        B  ) barfoo=${OPTARG}               ;;
        F  ) foobar=1 && foobar_name=${OPTARG} ;;
        h ) usagefull && exit 0 ;;
        : ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
        ? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
    esac
done
shift $((${OPTIND} - 1))

Oto test:

# Short options test
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2

# Long and short options test
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2

W przeciwnym razie W najnowszej powłoce Korn ksh93, getopts może w naturalny sposób parsować długie opcje, a nawet wyświetlać podobne strony podręcznika. (Patrz http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options )

 0
Author: Michel VONGVILAY uxora.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-19 13:33:33

Wbudowany system OS X (BSD) getopt nie obsługuje długich opcji, ale wersja GNU Tak: brew install gnu-getopt. Następnie coś podobnego do: cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt /usr/local/bin/gnu-getopt.

 0
Author: wprl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-18 15:15:24

Prosty DIY, aby uzyskać tylko długie nazwy args:

Użycie:

$ ./test-args.sh --a1 a1 --a2 "a 2" --a3 --a4= --a5=a5 --a6="a 6"
a1 = "a1"
a2 = "a 2"
a3 = "TRUE"
a4 = ""
a5 = "a5"
a6 = "a 6"
a7 = ""

Skrypt:

#!/bin/bash

function main() {
    ARGS=`getArgs "$@"`

    a1=`echo "$ARGS" | getNamedArg a1`
    a2=`echo "$ARGS" | getNamedArg a2`
    a3=`echo "$ARGS" | getNamedArg a3`
    a4=`echo "$ARGS" | getNamedArg a4`
    a5=`echo "$ARGS" | getNamedArg a5`
    a6=`echo "$ARGS" | getNamedArg a6`
    a7=`echo "$ARGS" | getNamedArg a7`

    echo "a1 = \"$a1\""
    echo "a2 = \"$a2\""
    echo "a3 = \"$a3\""
    echo "a4 = \"$a4\""
    echo "a5 = \"$a5\""
    echo "a6 = \"$a6\""
    echo "a7 = \"$a7\""

    exit 0
}


function getArgs() {
    for arg in "$@"; do
        echo "$arg"
    done
}


function getNamedArg() {
    ARG_NAME=$1

    sed --regexp-extended --quiet --expression="
        s/^--$ARG_NAME=(.*)\$/\1/p  # Get arguments in format '--arg=value': [s]ubstitute '--arg=value' by 'value', and [p]rint
        /^--$ARG_NAME\$/ {          # Get arguments in format '--arg value' ou '--arg'
            n                       # - [n]ext, because in this format, if value exists, it will be the next argument
            /^--/! p                # - If next doesn't starts with '--', it is the value of the actual argument
            /^--/ {                 # - If next do starts with '--', it is the next argument and the actual argument is a boolean one
                # Then just repla[c]ed by TRUE
                c TRUE
            }
        }
    "
}


main "$@"
 0
Author: lmcarreiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-11 15:26:14