2 Stimmen

Wie man getopts in bash mehrmals aufruft

Ich habe eine gemeinsame Bibliothek, die ich aus mehreren Skripten verwende, die Befehlszeilenoptionen parsen, aber ich möchte auch, dass meine einzelnen Skripte in der Lage sind, Argumente zu verarbeiten... z. B.

common.sh:

function get_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":ab:" optionName; do
       [ ... Bearbeitungscode ... ]
    done
}

a.sh

. ./common.sh

function get_local_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":xy:" optionName; do
       [ ... Bearbeitungscode ... ]
    done
}

get_local_options $*
OPTIND=1
get_options $*

Das Problem ist, dass, wenn ich a.sh mit aufrufe:

a.sh -x -y foo -a -b bar

get_options die Verarbeitung bei "foo" stoppt, da es am ersten "nicht-Option" stoppt

Gibt es eine Möglichkeit, dies zu umgehen, ohne selbst alles neu zu schreiben?

9voto

Bishop B Punkte 81
foo() {
  unset OPTIND
  while getopts ...
  do
  done
}

0 Stimmen

Gah Ich habe den dummen OPTIND vergessen!

1voto

Alok Singhal Punkte 87419

Ich habe es geschafft, dass es funktioniert, bin mir nicht sicher, ob das ist, was du willst:

$ cat common.sh
function get_options {
    while getopts ":ab:" optionName
    do
        echo "get_options: OPTARG: $OPTARG, optionName: $optionName"
    done
}
$ cat a.sh
#!/bin/bash

. ./common.sh

function get_local_options {
    while getopts ":xy:" optionName; do
        case $optionName in
            x|y) 
                echo "get_local_options: OPTARG: $OPTARG, optionName: $optionName"
                last=$OPTIND;;
            *) echo "get_local_options, done with $optionName"
                break;;
        esac;
    done
}

last=1
get_local_options $*
shift $(($last - 1))
OPTIND=1
get_options $*
$ ./a.sh -x -y foo -a -b bar
get_local_options: OPTARG: , optionName: x
get_local_options: OPTARG: foo, optionName: y
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b
$ ./a.sh -a -b bar
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b

1voto

ack Punkte 6878

Verwenden Sie einfach Ihr gemeinsames Skript, um eine Liste gemeinsamer Optionen und Funktionen zum Umgang damit bereitzustellen, und lassen Sie die anderen Skripte der Optionsliste alle Optionen hinzufügen, die sie verarbeiten können:

common.sh

OPTS="ab:h"

common_usage() {
  echo "Gemeinsame Optionen:"
  echo "  -a     ..."
  echo "  -b arg ..."
  echo "  -h     Zeige Verwendung"
  echo "  -v     Sei ausführlich"
}

handle_common_opts() {
  case "$1" in
    a) handle_opt_a ;;
    b) handle_opt_b "$2" ;;
    h) usage; exit 0 ;;
    v) be_verbose ;;
  esac
}

a.sh

. common.sh

OPTS="${OPTS}xy:"

usage() {
  echo "Verwendung: $PROG [Optionen] [Argumente...]"
  echo
  common_usage
  echo
  echo "Spezifische Optionen:"
  echo "  -x     ..."
  echo "  -y arg ..."
}

while getopts "$OPTS" OPT; do
  handle_common_opts "$OPT" "$OPTARG"
  case "$OPT" in
    x) handle_opt_x ;;
    y) handle_opt_y "$OPTARG" ;;
    \?) usage; exit 1 ;;
  esac
done
shift $((OPTIND-1))

handle_remaining_args "$@"

Dies hat folgende Vorteile: Es ist nicht notwendig, Tricks mit der internen Verwaltung von getopts zu spielen, der Benutzer kann gemeinsame und spezifische Optionen in beliebiger Reihenfolge angeben, es behandelt nicht erkannte Optionen an einer einzigen Stelle und erleichtert auch die Wartung der Verwendungsfunktionen.

0voto

dubiousjim Punkte 4604

Ich denke, du wirst deinen ersten Getopts-Prozessor brauchen, um eine Liste der Befehlszeilenelemente zu erstellen, die er nicht behandelt hat. Außerdem muss er den Rückgabewert des Getopts-Aufrufs ignorieren: Getopts könnte einen Nicht-Null-Wert zurückgeben, weil es auf ein Argument zu einem Befehlszeilenschalter stößt, das es nicht weiß, wie es verarbeiten soll. Der erste Getopts-Prozessor muss also ans Ende der Befehlszeile gehen. Du kannst seinen eigenen Einschätzungen nicht vertrauen, wann die Optionen aufgehört haben und die Argumente begonnen haben. (Nun ja, wenn du zwei Argumente hintereinander bekommst, könntest du vermutlich den Rest überspringen, aber das überlasse ich dir als Übung.)

Etwas in dieser Art:

#!/bin/bash

UNBEHANDELT=()

function getxy {
  while ((OPTIND<=$#)); do
    if getopts ":xy:" opt; then
      case $opt in
        x|y) echo "getxy opt=$<$opt> OPTARG=<$OPTARG>";;
        *) UNBEHANDELT+=(-$OPTARG);;
      esac
    else
        UNBEHANDELT+=(${!OPTIND})
        let OPTIND++
    fi
  done
}

function getab {
  while getopts ":ab:" opt; do
  case $opt in
    a|b) echo "getab opt=$<$opt> OPTARG=<$OPTARG>";;
    *) echo "getab * opt=<$opt> OPTARG=<$OPTARG>";;
  esac
  done
}

echo "--- getxy ---"
OPTIND=1
getxy "$@"
# jetzt setzen wir OPTIND zurück und analysieren erneut mit dem UNBEHANDELT-Array
echo "--- getab ---"
OPTIND=1
set -- "${UNBEHANDELT[@]}"
getab "$@"
# jetzt bekommen wir die verbleibenden Argumente
shift $((OPTIND-1))
for arg; do
    echo "arg=<$arg>"
done

0voto

Corin Punkte 2389

Es gibt hier einige gute Antworten, aber ich denke, es gibt eine einfache Lösung: Sie müssen OPTIND inkrementieren, anstatt es auf 1 zurückzusetzen. d.h.

a.sh

. ./common.sh

function get_local_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":xy:" optionName; do
       [ ... Verarbeitungscode ... ]
    done
}

get_local_options $*
(( OPTIND++ ))
get_options $*

Auf diese Weise überspringen Sie das Wort foo in der Args-Liste.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X