Wie gehe ich mit Switches in einem Shell-Skript um?

17

Gibt es einige integrierte Werkzeuge , die erkennen -xund --xxxxals Schalter, und nicht die Argumente, oder müssen Sie alle Eingangsgrößen Test für Striche durchlaufen, und dann die Argumente danach analysieren?

user394
quelle

Antworten:

16

Verwenden Sie getopts.

Es ist ziemlich portabel wie in der POSIX-Spezifikation. Leider unterstützt es keine langen Optionen.

Siehe auch dieses getopts Tutorial mit freundlicher Genehmigung des Bash-Hacker-Wikis und diese Frage von stackoverflow.

Wenn Sie nur kurze Optionen benötigen, getoptslautet das typische Verwendungsmuster für (bei Verwendung der nicht stillen Fehlerberichterstattung):

# process arguments "$1", "$2", ... (i.e. "$@")
while getopts "ab:" opt; do
    case $opt in
    a) aflag=true ;; # Handle -a
    b) barg=$OPTARG ;; # Handle -b argument
    \?) ;; # Handle error: unknown option or missing required argument.
    esac
done
jw013
quelle
3
Es sollte erwähnt werden, dass getoptes immer als GNU getopt verifiziert werden sollte, bevor Sie es verwenden, aber Sie sollten es sowieso nicht verwenden, da getoptses portabler (und im Allgemeinen netter) ist. Wenn Sie tun müssen es aus irgendeinem Grund nennen, nennen Sie es in einer GNU-spezifische Art und Weise, und dafür sorgen , dass GETOPT_COMPATIBLEnicht in die Umwelt.
Chris Down
Was macht der Doppelpunkt while getopts "ab:" opt?
User394
1
@ user394 :Nach einem Optionsbuchstaben ist ein Argument erforderlich. Ein :als erstes Zeichen bedeutet, Fehlermeldungen zu unterdrücken.
JW013
22

Ich nehme an, Sie verwenden Bash oder ähnliches. Ein Beispiel:

all=false
long=false

while getopts ":hal" option; do
  case $option in
    h) echo "usage: $0 [-h] [-a] [-l] file ..."; exit ;;
    a) all=true ;;
    l) long=true ;;
    ?) echo "error: option -$OPTARG is not implemented"; exit ;;
  esac
done

# remove the options from the positional parameters
shift $(( OPTIND - 1 ))

ls_opts=()
$all && ls_opts+=( -a )
$long && ls_opts+=( -l )

# now, do it
ls "${ls_opts[@]}" "$@"
Glenn Jackman
quelle
1
+1 für die Verwendung +=mit einem Array. Ich wusste nicht, dass du das machen kannst. Nett!
James Sneeringer
5

Sie müssen einen Zyklus schreiben, um die Parameter zu analysieren. In der Tat können Sie getoptsBefehl verwenden, um es einfach zu tun.

Dies ist ein einfaches Beispiel aus der getoptsManualpage:

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" "$*"
andcoz
quelle
2

Ich habe kürzlich ein Skript für die Arbeit geschrieben, das vielseitig ist und mehrere Arten von Schaltern in beliebiger Reihenfolge zulässt. Ich kann das vollständige Skript aus offensichtlichen rechtlichen Gründen nicht offenlegen (ganz zu schweigen davon, dass ich es im Moment nicht bei mir habe), aber hier ist das Fleisch davon. Sie können es in eine Unterroutine einfügen und am Ende aufrufen Ihres Skripts:

options () {

    if [ -n "$1" ]; then # test if any arguments passed - $1 will always exist
        while (( "$#" )); do  # process ALL arguments
            if [ "$1" = ^-t$ ]; then # -t short for "test"
                # do something here THEN shift the argument
                # shift removes it from $@ and reduces $# by
                # one if you supply no argument
                shift

            # we can also process multiple arguments at once
            elif [[ "$1" =~ ^--test=[:alnum:]{1,8}$ ]] && [[ "$2" =~ ^-t2$ ]] && [[ -n "$3" ]]; then # check for 3 arguments
                # do something more then remove them ALL from the arg list    
                shift 3
            else
                echo "No matching arguments!"
                echo "Usage: [script options list here]"
            fi
        done
    else
        echo "Usage: [script options list here]"
        exit 0
    fi
}

options "$@" # run options and loop through/process ALL arguments

Ich empfehle, Ihr Bash-Skript auf weniger als 400 Zeilen / 15.000 Zeichen zu beschränken. Mein oben genanntes Drehbuch wuchs über diese Größe hinaus und es wurde sehr schwierig, daran zu arbeiten. Ich habe angefangen, es in Perl umzuschreiben, das für die Aufgabe viel besser geeignet ist. Denken Sie daran, wenn Sie in bash an Ihren Skripten arbeiten. Bash ist großartig für kleine Skripte und Oneliners, aber alles, was komplexer ist, und Sie sollten es besser in Perl schreiben.

Beachten Sie, dass ich die obigen Tests nicht durchgeführt habe, sodass sie wahrscheinlich nicht funktionieren, aber Sie erhalten eine allgemeine Vorstellung davon.

laebshade
quelle
Die Art und Weise, wie Sie optionsam Ende anrufen, ist nicht korrekt, es wird zurückkehren -bash: syntax error near unexpected token $@. Nennen Sie es als options "$@".
Chris Down
Ja, ich habe Perl und Bash verwechselt. Korrigiert
Laebshade
Diese whileBedingung sollte nicht (($#))stattdessen sein?
Manatwork
Warum brauchen Sie zwei Klammern $#? Bearbeiten: du hast recht. while (( "$#" ))
Problem behoben