Spracovanie argumentov v shelli cez getopts

2019/01/07

Načo je dobrý getops?

getopts je posixový nástroj na spracovávanie prepínačov z príkazového riadka v rámci shellového skriptu. Vezmime si príklad:

./ffind.sh -s -t d java xml

Príkaz ffind.sh dostal päť argumentov, ktoré v skutočnosti reprezentujú tri rozličné druhy “vstupov”:

Ak by sme mali takéto argumenty spracovávať ručne, bolo by to šialené. Príkaz getopts sa s tým vysporiada veľmi jednoducho.

Zápis prepínačov

Príkaz getopts sa riadi nasledovnými pravidlami:

Použitie getopts

Príkaz getopts spracováva argument za argumentov z príkazového riadka (teda premenné 1, 2 atď) a sám si poradí so všetkými troma druhmi “vstupov”. Samotný príkaz getopts má dva základné argumenty:

Špecifikácia parametrov

V našom príklade bude špecifikácia vyzerať nasledovne:

:st:

Rozoberme si to znak po znaku:

Špecifikácia premennej so vstupom

Premennú, ktorá bude obsahovať práve spracovávaný prepínač alebo argument, si môžeme nazvať ľubovoľne, napr. OPT.

Použitie v kóde

Každé zavolanie getopts spracuje nasledovnú premennú z príkazového riadka. Pri prvom volaní sa spracuje premenná 1, pri druhom volaní premenná 2. Ak sa všetky premenné spracovali, getopts skončí s nenulovým návratovým kódom.

To je presne situácia, keď sa oplatí použiť cyklus while:

while getopts :st: OPT
do
...
done

Jednotlivé prepínače spracujeme v rámci príkazu case (ekvivalent switch z iných jazykov).

SILENT=''
TYPE=''
while getopts :st: OPT
do
  case "$OPT" in
      s) SILENT=1
         ;;
      t) TYPE="$OPTARG"
         ;;
      ?) echo "Neznamy parameter $OPT"
         ;;
  esac
done

V rozhodovaní sa pýtame na obsah premennej OPT, a podľa toho zistíme, ktorý prepínač chceme spracovať. V ukážke si podľa prepínača nastavíme príslušnú premennú.

Ak ide o prepínač s parametrom, v premennej OPTARG sa ocitne parameter tohto prepínača. V prípade prepínača -t d sa v premennej OPTARG objaví reťazec d.

Špeciálny prípad tvorí situácia, keď používateľ použije nepodporovaný prepínač (napr. -x). V takom prípade sa v premennej OPT ocitne otáznik. To je presne situácia na ručnú obsluhu nepodporovaných parametrov, čo zrealizujeme výpisom chybovej hlášky s názvom neznámeho parametra.

Použitie getopts a pozičných parametrov

V príklade sme mali dva parametre, ktoré neprislúchali žiadnemu prepínaču. Ide o parametre java a xml:

./ffind.sh -s -t d java xml

I na toto dokáže getopts myslieť. Príkaz si totiž počíta jednotlivé spracované parametre v špeciálnej číselnej premennej OPTIND a to môžeme použiť v skripte na zahodenie tých argumentov príkazového riadka, ktoré sa už spracovali.

Príklad? Pre parametre z ukážky si getopts naráta do premennej OPTIND štyri spracované položky.

shell $0 $1 $2 $3 $4 $5
./ffind.sh -s -t d java xml
OPTIND 1 2 3 4

Ak odčítame od OPTIND jednotku, získame akýsi index, o ktorý môžeme posunúť, teda shiftnúť premenné “doľava” a tým ich sprístupniť do shellových premenných 1, 2 atď.

V tomto prípade máme OPTIND rovný 4, a po posunutí o tri pozície, teda po použití príkazu shift $((OPTIND - 1)), získame nasledovný stav:

shell $0 $1 $2 $3 $4 $5
shell po shift $1 $2
./ffind.sh -s -t d java xml
OPTIND 1 2 3 4

Tento trik funguje pre ľubovoľný počet parametrov. Po shifte o príslušný počet miest sa indexované premenné nastavia na správne miesta. Zrazu budeme mať prvý pozičný parameter v premennej 1, druhý v premennej 2 atď, ba dokonca v univerzálnej premennej @ budeme mať všetky parametre pohromade a môžeme cez ne iterovať cyklom for, prípadne ich použiť ako parametrický vstup pre iné príkazy.

Celý skript

Výsledný skript so všetkými možnosťami vyzerá nasledovne:

SILENT=0
TYPE='unknown'
while getopts :st: OPT
do
  case "$OPT" in
          s) SILENT=1
             ;;
          t) TYPE="$OPTARG"
             ;;
          ?) echo "Unsupported parameter $OPT"
             ;;
  esac
done

shift $((OPTIND - 1))

echo "Silent mode: $SILENT"
echo "Type: $TYPE"
echo "Files:"

for FILE
do
  echo "$FILE"
done
>> Home