Ein Beispiel für die Verwendung von getopts in bash

345

Ich möchte die myscriptDatei folgendermaßen aufrufen :

$ ./myscript -s 45 -p any_string

oder

$ ./myscript -h  #should display help
$ ./myscript     #should display help

Meine Anforderungen sind:

  • getopt hier, um die Eingabeargumente zu erhalten
  • Überprüfen Sie -s, ob vorhanden, wenn kein Fehler zurückgegeben wird
  • Überprüfen Sie, ob der Wert nach -s45 oder 90 beträgt
  • Überprüfen Sie, ob das -pvorhanden ist und danach eine Eingabezeichenfolge vorhanden ist
  • Wenn der Benutzer die Hilfe eingibt ./myscript -hoder gerade ./myscriptanzeigt

Ich habe bisher diesen Code ausprobiert:

#!/bin/bash
while getopts "h:s:" arg; do
  case $arg in
    h)
      echo "usage" 
      ;;
    s)
      strength=$OPTARG
      echo $strength
      ;;
  esac
done

Aber mit diesem Code bekomme ich Fehler. Wie geht das mit Bash und getopt?

MOHAMED
quelle
2
Optionen sollten optional sein. Wenn Sie den von angegebenen Wert benötigen -s, machen Sie ihn zu einem Positionsargument : ./myscript 45 anystring.
Chepper
@chepner$./myscript -s 45 -p any_string
MOHAMED
Es ist in Ordnung, wenn dies -ptatsächlich eine Option ist (dh Ihr Programm kann fortgesetzt werden, wenn es nicht vorhanden ist). In diesem Fall,./myscript 45 -p any_string . (Ich denke, das getoptkann gemischte Optionen und Positionsargumente verarbeiten, während der basheingebaute Befehl getoptserfordert, dass alle Positionsargumente nach Optionen platziert werden.)
chepner

Antworten:

513
#!/bin/bash

usage() { echo "Usage: $0 [-s <45|90>] [-p <string>]" 1>&2; exit 1; }

while getopts ":s:p:" o; do
    case "${o}" in
        s)
            s=${OPTARG}
            ((s == 45 || s == 90)) || usage
            ;;
        p)
            p=${OPTARG}
            ;;
        *)
            usage
            ;;
    esac
done
shift $((OPTIND-1))

if [ -z "${s}" ] || [ -z "${p}" ]; then
    usage
fi

echo "s = ${s}"
echo "p = ${p}"

Beispielläufe:

$ ./myscript.sh
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -h
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s "" -p ""
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 10 -p foo
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 45 -p foo
s = 45
p = foo

$ ./myscript.sh -s 90 -p bar
s = 90
p = bar
Adrian Frühwirth
quelle
19
Warum gibt es im getopts-Aufruf einen führenden Doppelpunkt? Wann hat "h" einen Doppelpunkt danach?
e40
6
Sollte usage()wirklich 1 zurückgeben?
Pithikos
6
@ Pithikos Guter Punkt. Der gesunde Menschenverstand sagt mir, dass wenn es über -hes aufgerufen wird, es zurückkehren sollte 0, wenn es auf ein nicht existierendes Flag trifft >0(der Einfachheit halber habe ich nicht zwischen diesen Fällen unterschieden und niemand zwingt Sie, den Verwendungstext im letzteren Fall zu drucken). . Ich habe Programme gesehen , die immer wieder zurückkehren != 0, aber auch auf -h/--help. Vielleicht sollte ich das Snippet aktualisieren, falls Leute es als Boilerplate verwenden (ich hoffe nicht)?
Adrian Frühwirth
1
@ A.Danischewski Dies ist von ( getopts') Design, es gibt keine "optionalen Argumente" mit getopts. Der Parser kann einfach nicht wissen, ob das nächste Token ein Argument für die aktuelle Option oder eine Option für sich ist, da dies -pmöglicherweise der beabsichtigte Wert ist. Sie können dies umgehen, wenn Sie absolut wissen, dass ein Optionsparameter nicht wie eine andere gültige Option aussehen kann, ja, aber man könnte sagen, dass es einen Grund gibt, warum optionale Argumente in POSIX nicht definiert sind.
Adrian Frühwirth
4
@ user1011471 Du bist richtig! Geschweifte Klammern helfen dem bashLexer sozusagen nur bei der Identifizierung von Variablen. Sie werden in vielen Fällen nicht benötigt und die Tatsache, dass ich sie immer benutze, ist nur eine Frage des persönlichen Codierungsstils. Für mich ist es einfacher (und hübscher), sie einfach immer zu verwenden, anstatt sich an die Parsing-Regeln in Bezug auf Mehrdeutigkeiten zu erinnern. Ziemlich genau das gleiche, warum man if (foo) { bar; }statt if (foo) bar;in einer Sprache im C-Stil schreiben würde (Ästhetik und / oder Vermeidung alberner Fehler).
Adrian Frühwirth
109

Das Problem mit dem Originalcode ist, dass:

  • h:erwartet Parameter, wo es nicht sollte, also ändern Sie es in nur h(ohne Doppelpunkt)
  • Um zu erwarten -p any_string, müssen Sie p:der Argumentliste hinzufügen

Grundsätzlich :bedeutet die Option, dass das Argument erforderlich ist.


Die grundlegende Syntax von getoptsist (siehe :) man bash:

getopts OPTSTRING VARNAME [ARGS...]

wo:

  • OPTSTRING ist eine Zeichenfolge mit einer Liste der erwarteten Argumente.

    • h- auf Option -h ohne Parameter prüfen ; gibt Fehler bei nicht unterstützten Optionen aus;
    • h:- Option -h mit Parameter prüfen ; gibt Fehler bei nicht unterstützten Optionen an;
    • abc- Check für Optionen -a, -b, -c; gibt Fehler bei nicht unterstützten Optionen an;
    • :abc- Check für Optionen -a, -b, -c; Stummschalten von Fehlern bei nicht unterstützten Optionen;

      Hinweise: Mit anderen Worten, der Doppelpunkt vor den Optionen ermöglicht es Ihnen, die Fehler in Ihrem Code zu behandeln. Die Variable enthält ?bei nicht unterstützten Optionen und :bei fehlenden Werten.

  • OPTARG - wird auf den aktuellen Argumentwert gesetzt,

  • OPTERR - Gibt an, ob Bash Fehlermeldungen anzeigen soll.

Der Code kann also sein:

#!/usr/bin/env bash
usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; }
[ $# -eq 0 ] && usage
while getopts ":hs:p:" arg; do
  case $arg in
    p) # Specify p value.
      echo "p is ${OPTARG}"
      ;;
    s) # Specify strength, either 45 or 90.
      strength=${OPTARG}
      [ $strength -eq 45 -o $strength -eq 90 ] \
        && echo "Strength is $strength." \
        || echo "Strength needs to be either 45 or 90, $strength found instead."
      ;;
    h | *) # Display help.
      usage
      exit 0
      ;;
  esac
done

Anwendungsbeispiel:

$ ./foo.sh 
./foo.sh usage:
    p) # Specify p value.
    s) # Specify strength, either 45 or 90.
    h | *) # Display help.
$ ./foo.sh -s 123 -p any_string
Strength needs to be either 45 or 90, 123 found instead.
p is any_string
$ ./foo.sh -s 90 -p any_string
Strength is 90.
p is any_string

Siehe: Kleine getopts Tutorial im Bash Hackers Wiki

Kenorb
quelle
2
Ändern Sie die Verwendungsfunktion in : usage() { echo "$0 usage:" && grep "[[:space:]].)\ #" $0 | sed 's/#//' | sed -r 's/([a-z])\)/-\1/'; exit 0; }. Es berücksichtigt nur ein einzelnes Leerzeichen vor der Buchstabenoption, entfernt das # aus dem Kommentar und stellt ein '-' vor die Buchstabenoption, um den Befehl klarer zu machen.
Poagester
2
@kenorb: Doppelpunkt vor Optionen ignoriert nicht unterstützte Optionen nicht, stummschaltet jedoch Fehler von Bash und ermöglicht es Ihnen, sie in Ihrem Code zu behandeln. Die Variable enthält '?' bei nicht unterstützter Option und ':' bei fehlendem Wert.
Hynek-Pichi-Vychodil
1
Vielen Dank für die detaillierten Dokumente, :ich konnte nicht das Richtige finden, bis ich diese Notizen sah. Wir müssen :den Optionen, bei denen wir ein Argument erwarten, ein hinzufügen .
Aukhan
51

Verwenden getopt

Warum getopt?

Um ausgearbeitete Befehlszeilenargumente zu analysieren, um Verwirrung zu vermeiden und die Optionen zu klären, die wir analysieren, damit der Leser der Befehle verstehen kann, was passiert.

Was ist getopt?

getoptwird verwendet, um Optionen in Befehlszeilen aufzuteilen (zu analysieren), um das Parsen durch Shell-Prozeduren zu vereinfachen und um nach legalen Optionen zu suchen. Dazu werden die GNU- getopt(3)Routinen verwendet.

getopt kann folgende Arten von Optionen haben.

  1. Optionen ohne Wert
  2. Schlüssel-Wert-Paar-Optionen

Hinweis: In diesem Dokument wird während der Erläuterung der Syntax Folgendes erläutert:

  • Alles in [] ist ein optionaler Parameter in der Syntax / den Beispielen.
  • ist ein Platzhalter, was bedeutet, dass er durch einen tatsächlichen Wert ersetzt werden sollte.

WIE BENUTZEN getopt?

Syntax: Erste Form

getopt optstring parameters

Beispiele:

# This is correct
getopt "hv:t::" "-v 123 -t123"  
getopt "hv:t::" "-v123 -t123"  # -v and 123 doesn't have whitespace

# -h takes no value.
getopt "hv:t::" "-h -v123"


# This is wrong. after -t can't have whitespace.
# Only optional params cannot have whitespace between key and value
getopt "hv:t::" "-v 123 -t 123"

# Multiple arguments that takes value.
getopt "h:v:t::g::" "-h abc -v 123 -t21"

# Multiple arguments without value
# All of these are correct
getopt "hvt" "-htv"
getopt "hvt" "-h -t -v"
getopt "hvt" "-tv -h"

Hier sind h, v, t die Optionen und -h -v -t ist, wie Optionen in der Befehlszeile angegeben werden sollen.

  1. 'h' ist eine Option ohne Wert.
  2. 'v:' impliziert, dass die Option -v einen Wert hat und eine obligatorische Option ist. ':' bedeutet hat einen Wert.
  3. 't ::' impliziert, dass die Option -t einen Wert hat, aber optional ist. '::' bedeutet optional.

In optionalen Parametern kann value mit der Option keine Leerzeichen-Trennung aufweisen. Im Beispiel "-t123" ist -t also Option 123 ist Wert.

Syntax: Zweite Form

getopt [getopt_options] [--] [optstring] [parameters]

Hier wird getopt in fünf Teile geteilt

  • Der Befehl selbst dh getopt
  • In getopt_options wird beschrieben, wie die Argumente analysiert werden. Einzelstrich lange Optionen, Doppelstrichoptionen.
  • - trennt die getopt_options von den Optionen, die Sie analysieren möchten, und den zulässigen kurzen Optionen
  • Die kurzen Optionen werden sofort nach - gefunden. Genau wie die erste Syntax von Form.
  • Die Parameter, dies sind die Optionen, die Sie an das Programm übergeben haben. Die Optionen, die Sie analysieren und die tatsächlichen Werte abrufen möchten.

Beispiele

getopt -l "name:,version::,verbose" -- "n:v::V" "--name=Karthik -version=5.2 -verbose"

Syntax: Dritte Form

getopt [getopt_options] [-o options] [--] [optstring] [parameters]

Hier wird getopt in fünf Teile geteilt

  • Der Befehl selbst dh getopt
  • In getopt_options wird beschrieben, wie die Argumente analysiert werden. Einzelstrich lange Optionen, Doppelstrichoptionen.
  • Die kurzen Optionen sind -o oder --options. Genau wie die erste Syntax des Formulars, jedoch mit der Option "-o" und vor dem "-" (doppelter Bindestrich).
  • - trennt die getopt_options von den Optionen, die Sie analysieren möchten, und den zulässigen kurzen Optionen
  • Die Parameter, dies sind die Optionen, die Sie an das Programm übergeben haben. Die Optionen, die Sie analysieren und die tatsächlichen Werte abrufen möchten.

Beispiele

getopt -l "name:,version::,verbose" -a -o "n:v::V" -- "-name=Karthik -version=5.2 -verbose"

GETOPT_OPTIONS

getopt_options ändert die Art und Weise, wie Befehlszeilenparameter analysiert werden.

Nachfolgend einige der getopt_options

Option: -l oder --longoptions

Der Befehl getopt sollte die Erkennung von Optionen mit mehreren Zeichen ermöglichen. Mehrere Optionen werden durch Komma getrennt.

Beispielsweise --name=Karthikwird eine lange Option in der Befehlszeile gesendet. In getopt werden lange Optionen verwendet

getopt "name:,version" "--name=Karthik"

Da name: angegeben ist, sollte die Option einen Wert enthalten

Option: -a oder --alternative

Der Befehl getopt sollte es ermöglichen, dass die lange Option einen einzelnen Bindestrich '-' anstelle eines doppelten Bindestrichs '-' enthält.

Beispiel, anstatt --name=KarthikSie könnten nur verwenden-name=Karthik

getopt "name:,version" "-name=Karthik"

Ein vollständiges Skriptbeispiel mit dem Code:

#!/bin/bash

# filename: commandLine.sh
# author: @theBuzzyCoder

showHelp() {
# `cat << EOF` This means that cat should stop reading when EOF is detected
cat << EOF  
Usage: ./installer -v <espo-version> [-hrV]
Install Pre-requisites for EspoCRM with docker in Development mode

-h, -help,          --help                  Display help

-v, -espo-version,  --espo-version          Set and Download specific version of EspoCRM

-r, -rebuild,       --rebuild               Rebuild php vendor directory using composer and compiled css using grunt

-V, -verbose,       --verbose               Run script in verbose mode. Will print out each step of execution.

EOF
# EOF is found above and hence cat command stops reading. This is equivalent to echo but much neater when printing out.
}


export version=0
export verbose=0
export rebuilt=0

# $@ is all command line parameters passed to the script.
# -o is for short options like -v
# -l is for long options with double dash like --version
# the comma separates different long options
# -a is for long options with single dash like -version
options=$(getopt -l "help,version:,verbose,rebuild,dryrun" -o "hv:Vrd" -a -- "$@")

# set --:
# If no arguments follow this option, then the positional parameters are unset. Otherwise, the positional parameters 
# are set to the arguments, even if some of them begin with a ‘-’.
eval set -- "$options"

while true
do
case $1 in
-h|--help) 
    showHelp
    exit 0
    ;;
-v|--version) 
    shift
    export version=$1
    ;;
-V|--verbose)
    export verbose=1
    set -xv  # Set xtrace and verbose mode.
    ;;
-r|--rebuild)
    export rebuild=1
    ;;
--)
    shift
    break;;
esac
shift
done

Ausführen dieser Skriptdatei:

# With short options grouped together and long option
# With double dash '--version'

bash commandLine.sh --version=1.0 -rV
# With short options grouped together and long option
# With single dash '-version'

bash commandLine.sh -version=1.0 -rV

# OR with short option that takes value, value separated by whitespace
# by key

bash commandLine.sh -v 1.0 -rV

# OR with short option that takes value, value without whitespace
# separation from key.

bash commandLine.sh -v1.0 -rV

# OR Separating individual short options

bash commandLine.sh -v1.0 -r -V
theBuzzyCoder
quelle
Quelle: linkedin.com/pulse/…
theBuzzyCoder
getopt vs getopts .. sehr unterschiedliche plattformübergreifende Compliance
shadowbq
35

Das mit verpackte Beispiel getopt(meine Distribution hat es eingefügt /usr/share/getopt/getopt-parse.bash) sieht so aus, als würde es alle Ihre Fälle abdecken:

#!/bin/bash

# A small example program for using the new getopt(1) program.
# This program will only work with bash(1)
# An similar program using the tcsh(1) script language can be found
# as parse.tcsh

# Example input and output (from the bash prompt):
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
# Option a
# Option c, no argument
# Option c, argument `more'
# Option b, argument ` very long '
# Remaining arguments:
# --> `par1'
# --> `another arg'
# --> `wow!*\?'

# Note that we use `"$@"' to let each command-line parameter expand to a 
# separate word. The quotes around `$@' are essential!
# We need TEMP as the `eval set --' would nuke the return value of getopt.
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n 'example.bash' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

while true ; do
    case "$1" in
        -a|--a-long) echo "Option a" ; shift ;;
        -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
        -c|--c-long) 
            # c has an optional argument. As we are in quoted mode,
            # an empty parameter will be generated if its optional
            # argument is not found.
            case "$2" in
                "") echo "Option c, no argument"; shift 2 ;;
                *)  echo "Option c, argument \`$2'" ; shift 2 ;;
            esac ;;
        --) shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done
echo "Remaining arguments:"
for arg do echo '--> '"\`$arg'" ; done
Brian Cain
quelle
11
Der externe Befehl getopt (1) ist niemals sicher zu verwenden, es sei denn, Sie wissen, dass es sich um GNU getopt handelt, Sie rufen ihn GNU-spezifisch auf und stellen sicher, dass sich GETOPT_COMPATIBLE nicht in der Umgebung befindet. Verwenden Sie stattdessen getopts (Shell Builtin) oder durchlaufen Sie einfach die Positionsparameter.
Gilles Quenot
@sputnick, tyvm, wusste das nicht.
Brian Cain
14
Eh, kein externer Befehl kann von diesem Standard sicher verwendet werden. In getopts fehlen wichtige Funktionen. Wenn Sie nach GETOPT_COMPATIBLE suchen möchten, ist dies einfacher als das Portieren der Funktionen von getopt.
Michael Terry
12

Ich weiß, dass dies bereits beantwortet wurde, aber für die Aufzeichnung und für jeden mit den gleichen Anforderungen wie ich habe ich beschlossen, diese verwandte Antwort zu posten. Der Code wird mit Kommentaren überflutet, um den Code zu erklären.

Aktualisierte Antwort:

Speichern Sie die Datei als getopt.sh:

#!/bin/bash

function get_variable_name_for_option {
    local OPT_DESC=${1}
    local OPTION=${2}
    local VAR=$(echo ${OPT_DESC} | sed -e "s/.*\[\?-${OPTION} \([A-Z_]\+\).*/\1/g" -e "s/.*\[\?-\(${OPTION}\).*/\1FLAG/g")

    if [[ "${VAR}" == "${1}" ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

function parse_options {
    local OPT_DESC=${1}
    local INPUT=$(get_input_for_getopts "${OPT_DESC}")

    shift
    while getopts ${INPUT} OPTION ${@};
    do
        [ ${OPTION} == "?" ] && usage
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
            [ "${VARNAME}" != "" ] && eval "${VARNAME}=${OPTARG:-true}" # && printf "\t%s\n" "* Declaring ${VARNAME}=${!VARNAME} -- OPTIONS='$OPTION'"
    done

    check_for_required "${OPT_DESC}"

}

function check_for_required {
    local OPT_DESC=${1}
    local REQUIRED=$(get_required "${OPT_DESC}" | sed -e "s/\://g")
    while test -n "${REQUIRED}"; do
        OPTION=${REQUIRED:0:1}
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
                [ -z "${!VARNAME}" ] && printf "ERROR: %s\n" "Option -${OPTION} must been set." && usage
        REQUIRED=${REQUIRED:1}
    done
}

function get_input_for_getopts {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_optional {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/[^[]*\(\[[^]]*\]\)[^[]*/\1/g" -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_required {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/\[[^[]*\]//g" -e "s/[][ -]//g"
}

function usage {
    printf "Usage:\n\t%s\n" "${0} ${OPT_DESC}"
    exit 10
}

Dann können Sie es so verwenden:

#!/bin/bash
#
# [ and ] defines optional arguments
#

# location to getopts.sh file
source ./getopt.sh
USAGE="-u USER -d DATABASE -p PASS -s SID [ -a START_DATE_TIME ]"
parse_options "${USAGE}" ${@}

echo ${USER}
echo ${START_DATE_TIME}

Alte Antwort:

Ich musste kürzlich einen generischen Ansatz verwenden. Ich bin auf diese Lösung gestoßen:

#!/bin/bash
# Option Description:
# -------------------
#
# Option description is based on getopts bash builtin. The description adds a variable name feature to be used
# on future checks for required or optional values.
# The option description adds "=>VARIABLE_NAME" string. Variable name should be UPPERCASE. Valid characters
# are [A-Z_]*.
#
# A option description example:
#   OPT_DESC="a:=>A_VARIABLE|b:=>B_VARIABLE|c=>C_VARIABLE"
#
# -a option will require a value (the colon means that) and should be saved in variable A_VARIABLE.
# "|" is used to separate options description.
# -b option rule applies the same as -a.
# -c option doesn't require a value (the colon absense means that) and its existence should be set in C_VARIABLE
#
#   ~$ echo get_options ${OPT_DESC}
#   a:b:c
#   ~$
#


# Required options 
REQUIRED_DESC="a:=>REQ_A_VAR_VALUE|B:=>REQ_B_VAR_VALUE|c=>REQ_C_VAR_FLAG"

# Optional options (duh)
OPTIONAL_DESC="P:=>OPT_P_VAR_VALUE|r=>OPT_R_VAR_FLAG"

function usage {
    IFS="|"
    printf "%s" ${0}
    for i in ${REQUIRED_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
    printf " %s" "-${i:0:1} $VARNAME"
    done

    for i in ${OPTIONAL_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
        printf " %s" "[-${i:0:1} $VARNAME]"
    done
    printf "\n"
    unset IFS
    exit
}

# Auxiliary function that returns options characters to be passed
# into 'getopts' from a option description.
# Arguments:
#   $1: The options description (SEE TOP)
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   OPTIONS=$(get_options ${OPT_DESC})
#   echo "${OPTIONS}"
#
# Output:
#   "h:f:PW"
function get_options {
    echo ${1} | sed -e "s/\([a-zA-Z]\:\?\)=>[A-Z_]*|\?/\1/g"
}

# Auxiliary function that returns all variable names separated by '|'
# Arguments:
#       $1: The options description (SEE TOP)
#
# Example:
#       OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#       VARNAMES=$(get_values ${OPT_DESC})
#       echo "${VARNAMES}"
#
# Output:
#       "H_VAR|F_VAR|P_VAR|W_VAR"
function get_variables {
    echo ${1} | sed -e "s/[a-zA-Z]\:\?=>\([^|]*\)/\1/g"
}

# Auxiliary function that returns the variable name based on the
# option passed by.
# Arguments:
#   $1: The options description (SEE TOP)
#   $2: The option which the variable name wants to be retrieved
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   H_VAR=$(get_variable_name ${OPT_DESC} "h")
#   echo "${H_VAR}"
#
# Output:
#   "H_VAR"
function get_variable_name {
    VAR=$(echo ${1} | sed -e "s/.*${2}\:\?=>\([^|]*\).*/\1/g")
    if [[ ${VAR} == ${1} ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

# Gets the required options from the required description
REQUIRED=$(get_options ${REQUIRED_DESC})

# Gets the optional options (duh) from the optional description
OPTIONAL=$(get_options ${OPTIONAL_DESC})

# or... $(get_options "${OPTIONAL_DESC}|${REQUIRED_DESC}")

# The colon at starts instructs getopts to remain silent
while getopts ":${REQUIRED}${OPTIONAL}" OPTION
do
    [[ ${OPTION} == ":" ]] && usage
    VAR=$(get_variable_name "${REQUIRED_DESC}|${OPTIONAL_DESC}" ${OPTION})
    [[ -n ${VAR} ]] && eval "$VAR=${OPTARG}"
done

shift $(($OPTIND - 1))

# Checks for required options. Report an error and exits if
# required options are missing.

# Using function version ...
VARS=$(get_variables ${REQUIRED_DESC})
IFS="|"
for VARNAME in $VARS;
do
    [[ -v ${VARNAME} ]] || usage
done
unset IFS

# ... or using IFS Version (no function)
OLDIFS=${IFS}
IFS="|"
for i in ${REQUIRED_DESC};
do
    VARNAME=$(echo $i | sed -e "s/.*=>//g")
    [[ -v ${VARNAME} ]] || usage
    printf "%s %s %s\n" "-${i:0:1}" "${!VARNAME:=present}" "${VARNAME}"
done
IFS=${OLDIFS}

Ich habe das nicht grob getestet, also könnte ich ein paar Fehler darin haben.

Sebastian
quelle
1
Wenn Sie getoptsin einer Funktion verwenden, fügen Sie local OPTIND OPTARGder Funktion hinzu
Glenn Jackman
@glennjackman eigentlich ist es eher ein sed Ansatz als zu verwendengetopts
Sebastian
8

POSIX 7 Beispiel

Es lohnt sich auch, das Beispiel aus dem Standard zu überprüfen: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)   printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"

Und dann können wir es ausprobieren:

$ sh a.sh
Remaining arguments are: 
$ sh a.sh -a
Option -a specified
Remaining arguments are: 
$ sh a.sh -b
No arg for -b option
Usage: a.sh: [-a] [-b value] args
$ sh a.sh -b myval
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh -a -b myval
Option -a specified
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh remain
Remaining arguments are: remain
$ sh a.sh -- -a remain
Remaining arguments are: -a remain

In Ubuntu 17.10 getestet, shist Bindestrich 0.5.8.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
0

"getops" und "getopt" sind sehr begrenzt. Während "getopt" empfohlen wird, überhaupt nicht verwendet zu werden, bietet es lange Optionen. Wobei "getopts" nur Einzelzeichenoptionen wie "-a" "-b" zulässt. Es gibt noch ein paar weitere Nachteile, wenn Sie einen von beiden verwenden.

Also habe ich ein kleines Skript geschrieben, das "getopts" und "getopt" ersetzt. Es ist ein Anfang, es könnte wahrscheinlich viel verbessert werden.

Update 08-04-2020 : Ich habe Unterstützung für Bindestriche hinzugefügt, z. B. "--package-name".

Verwendung: "./script.sh package install --package" name with space "--build --archive"

# Example:
# parseArguments "${@}"
# echo "${ARG_0}" -> package
# echo "${ARG_1}" -> install
# echo "${ARG_PACKAGE}" -> "name with space"
# echo "${ARG_BUILD}" -> 1 (true)
# echo "${ARG_ARCHIVE}" -> 1 (true)
function parseArguments() {
  PREVIOUS_ITEM=''
  COUNT=0
  for CURRENT_ITEM in "${@}"
  do
    if [[ ${CURRENT_ITEM} == "--"* ]]; then
      printf -v "ARG_$(formatArgument "${CURRENT_ITEM}")" "%s" "1" # could set this to empty string and check with [ -z "${ARG_ITEM-x}" ] if it's set, but empty.
    else
      if [[ $PREVIOUS_ITEM == "--"* ]]; then
        printf -v "ARG_$(formatArgument "${PREVIOUS_ITEM}")" "%s" "${CURRENT_ITEM}"
      else
        printf -v "ARG_${COUNT}" "%s" "${CURRENT_ITEM}"
      fi
    fi

    PREVIOUS_ITEM="${CURRENT_ITEM}"
    (( COUNT++ ))
  done
}

# Format argument.
function formatArgument() {
  ARGUMENT="${1^^}" # Capitalize.
  ARGUMENT="${ARGUMENT/--/}" # Remove "--".
  ARGUMENT="${ARGUMENT//-/_}" # Replace "-" with "_".
  echo "${ARGUMENT}"
}
Twoez
quelle