ubuntuusers.de

getopts

Status: Gelöst | Ubuntu-Version: Ubuntu MATE 22.04 (Jammy Jellyfish)
Antworten |

CarstenHa

Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 138

Hallo liebes Forum,

ich habe da mal eine Frage:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash

while getopts a:lc: opt
do
   case $opt in
       a) echo "$OPTARG"
       ;;
       l) shift $(("$OPTIND" - 1))
          while [[ ! "$1" =~ ^- ]] && [ ! "$#" == "0" ]; do
           lstring+="${1} "
           shift
          done
          echo "${lstring% }"
       ;;
       c) echo "$OPTARG"
       ;;
   esac
done

Wie mache ich das am Besten, wenn ich das Beispiel wie folgt aufrufe:

1
./skriptname.sh -a Hallo -l a b c d e -c Welt

das mir die Option -c und das dazugehörende OPTARG erhalten bleibt. Bis jetzt habe ich mich vor dieser Aufgabe erfolgreich drücken können. Ich würde das aber doch gerne wissen 😀

Ich weiß, ich könnte einfach folgendes eingeben:

1
./skriptname.sh -a Hallo -c Welt -l a b c d e

Aber für das erste Beispiel gibt es doch bestimmt eine Möglichkeit, oder?

Viele Grüße

Carsten

frostschutz

Avatar von frostschutz

Anmeldungsdatum:
18. November 2010

Beiträge: 7782

Mir ist nicht so ganz klar was das bei l) soll. Du versuchst da doch verschiedene Parsing-Ansätze zu mischen mit entsprechendem Ausgang.

Statt da mit Aufwand den lstring zusammen zu bauen, sollte das Programm direkt mit -l "a b c d e" aufgerufen werden, oder nicht?

Nach shift $OPTINT stimmt OPTIND auch nicht mehr, das müsstest du dementsprechend auch auf 1 zurücksetzen. Dann klappt es vielleicht auch wieder mit -c, aber...

getopt / getopts macht eigentlich am meisten Sinn, wenn man sich auf die Limitierungen einlässt. Nur dann verhalten sich die Optionen halbwegs, so wie man es von anderen Befehlen auch kennt.

Was will dein Programm denn sonst z.B. bei -l a b -c Welt d e machen? Idealerweise wäre es das gleiche wie -l a b d e -c Welt aber mit deiner Shift Kette zwischendrin kommt es so nicht mehr durch.

CarstenHa

(Themenstarter)
Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 138

Ja, danke. OPTIND auf 1 setzen. So einfach kann es sein. Danke ☺

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13166

Danke für den Anlass, mal etwas zu experimentieren. Eine Variante wäre OPTIND zu erhöhen, während es ein weiteres Argument für -l gibt und erst ganz am Ende mit shift schieben, so wie man das immer nach einer getopts-Schleife macht:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

while getopts ':a:l:c:' opt; do
  case "$opt" in
  a)  echo "a=$OPTARG"
      ;;
  l)  lstring="$OPTARG"

      while eval "v=\$$OPTIND"; [ -n "${v##-*}" ]; do
        lstring+=" $v"
        : $((OPTIND += 1))
      done
      ;;
  c)  echo "c=$OPTARG"
      ;;
  *)  echo "ERROR: unknown option $OPTARG"
      exit 1
      ;;
  esac
done

shift $((OPTIND - 1))

echo "args: $*"

Ich weiß ja nicht, was Du vorhast, aber es läge nahe, lstring zu einem Array zu machen. Generell stimme ich aber hier zu:

frostschutz schrieb:

Du versuchst da doch verschiedene Parsing-Ansätze zu mischen mit entsprechendem Ausgang.

getopt / getopts macht eigentlich am meisten Sinn, wenn man sich auf die Limitierungen einlässt. Nur dann verhalten sich die Optionen halbwegs, so wie man es von anderen Befehlen auch kennt.

Man kann dann auch gleich das Parsen der Optionen selber machen - darauf läuft es m.E. hinaus.

CarstenHa

(Themenstarter)
Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 138

Ich stimme Euch zu. In der Regel sollte man sich auf die Limitierungen einlassen. Aber manchmal macht es einfach Spass, die Möglichkeiten "kreativ" auszuschöpfen 😉

Es gab jetzt auch keinen direkten Anlass, die Idee ging mir halt nicht aus dem Kopf und dann probiere ich gerne mal aus, um Fehlerquellen in zukünftigen Programmen möglichst auszuschliessen.

Deswegen ist das folgende Beispiel nur mal so zum rumprobieren, poste es aber trotzdem mal für alle die es interessiert:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/bash

# ./opt.sh -z "eine eins" zwei drei -a Hallo -l a b c -c Welt

while getopts a:lc:z opt
do
   case $opt in
       a) echo "$OPTARG"
       ;;
       l) # Diese Option benötigt mindestens 1 bis beliebig viele Argumente.
          # Argumente dürfen NICHT mit einem '-[aclz]' beginnen.
          shift $(("$OPTIND" - 1))
          while [[ ! "$1" =~ ^-[aclz] ]] && [ ! "$#" == "0" ]; do
           lstring+="${1} "
           shift
          done
          if [ -z "$lstring" ]; then
           echo "Fehler Option -${opt}. Bitte mindestens ein Argument angeben."
           exit 1
          else
           echo "${lstring% }"
          fi
          OPTIND=1
       ;;
       c) echo "$OPTARG"
       ;;
       z) # Diese Option benötigt genau 3 Argumente.
          # Argumente dürfen mit einem '-' beginnen.
          # Shiften der Option
          shift $(("$OPTIND" - 1))
          echo "$1"
          echo "$2"
          echo "$3"
          # Shiften der drei Parameter und OPTIND wieder auf 1 setzen, damit weitere Optionen ausgewertet werden können.
          shift 3
          OPTIND=1
          # Überprüfung, ob nächstes Argument wirklich eine Option ist.
          if [[ ! "$1" =~ ^-[aclz] ]] && [ ! "$#" == "0" ]; then
           echo "Fehler Option -${opt}."
           exit 1
          fi
       ;;
       *) exit 1
       ;;
   esac
done

@rklm Mit dem Array wäre jetzt auch übrigens meine weitere Vorgenhensweise gewesen.

Viele Grüße

Carsten

CarstenHa

(Themenstarter)
Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 138

Wer noch ein paar Befehle zum Ausprobieren braucht 😉

1
2
./opt.sh -z "eine eins" zwei drei vier -a Hallo -l a b c -6 -c Welt # gibt eine gewünschte Fehlermeldung
./opt.sh -z "eine eins" zwei drei -a Hallo -l a b c -6 -c Welt # Läuft ohne Fehler durch

Viel Spass!

CarstenHa

(Themenstarter)
Avatar von CarstenHa

Anmeldungsdatum:
1. Mai 2020

Beiträge: 138

rklm schrieb:

... und erst ganz am Ende mit shift schieben, so wie man das immer nach einer getopts-Schleife macht:

Danke für den Tipp 👍

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 9615

Wohnort: Münster

frostschutz schrieb:

[…] getopt / getopts macht […]

Die beiden sind übrigens nicht dasselbe, obwohl ihre Namen Verwechslungen sehr begünstigen:

$ type getopt getopts
getopt ist /usr/bin/getopt
getopts ist eine von der Shell mitgelieferte Funktion.

Und sie machen auch etwas sehr ähnliches, aber in Details doch nicht dasselbe. Siehe Manpages.

Antworten |