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”:
-steda prepínač-treprezentujúci prepínač s parametrom (d)- dva nepomenované argumenty
javaaxml
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:
-
prepínače majú jednopísmenové označenia, napr.
-salebo-t. Dlhé parametre (long options) v duchu GNU (—all) nie sú podporované. (To je doménou vylepšeného príkazugetopt, ktorý ale nie je posixový). -
prepínače možno zoskupovať. Nasledovný zápis je rovnaký ako z úvodnej ukážky:
./ffind.sh -st d java xml -
prepínače možno uvádzať v ľubovoľnom poradí:
./ffind.sh -t d -s java xml
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áciu podporovaných argumentov
- premennú, do ktorej postupne priraďuje jednotlivé prepínače.
Špecifikácia parametrov
V našom príklade bude špecifikácia vyzerať nasledovne:
:st:
Rozoberme si to znak po znaku:
:Dvojbodka na začiatku vypne automatické vypisovanie chybových hlášok pre parametre, ktoré nedokážeme spracovať. Túto obsluhu si však veľmi jednoducho vieme urobiť sami.s: Prepínačsneberie parametre, preto ho uvedieme len názvom.t:Prepínačt, ktorý berie parameter, uvedieme s dvojbodkou. Dvojbodka tu má dva významy: jednak vypína chybové hlášky (ak je na začiatku) a jednak špecifikuje prepínač s argumentami (ak je uvedená za ním).
Š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