7. diel - Céčko a Linux - Prepínače a getopt
Skúšky skončili a ja si konečne našiel čas na spísanie ďalšieho dielu
V minulej lekcii, Céčko a Linux - Filtre , sme si povedali niečo o tom, ako funguje odovzdávanie argumentov zo shellu a ako ich môže náš program spracovať. Dnes si vyskúšame meniť správanie programu pomocou prepínačov a napíšeme si jednoduchú demonštračné aplikáciu.
Prepínače
Prepínač je argument programu, ktorý typicky ovplyvňuje (prepína) jeho funkcionalitu. Napríklad majme program, ktorý dostane tri argumenty. Prvý bude určovať operáciu, ktorá bude vykonaná s nasledujúcimi dvoma argumentmi.
Prepínače obykle začínajú znakom -
pre skrátenú podobu a
dvoma znakmi -
pre neskrátenú podobu. Môžeme si samozrejme
vymyslieť akýkoľvek formát, ale toto je taký štandard a myslím, že nie
je dôvod ho nepoužívať.
Pre začiatok si zadefinujeme dva prepínače:
- sčítanie:
-a
resp.--addition
- násobenie:
-m
resp.--multiplication
Program potom spustíme napríklad takto:
$ ./program -a 10 5
Ukážka
Ako prvú vec musíme určite overiť, či používateľ zadal správny počet argumentov. Keby sme to neurobili, program by pri nesprávnom použití pracoval neočakávane a užívateľ by o tom nebol informovaný.
if (argc != 4) { fprintf(stderr, "Chybny pocet argumentu!\nNapovedu zobrazite pomoci prepinace -h\n"); return 1; }
Nezabudnime, že prvý argument je názov programu, takže musíme overovať
na počet zadávaných argumentov + 1. Vidíme tiež, že by sa asi hodil
ďalší prepínač pre zobrazenie pomocníka. Použijeme štandardné
-h
, resp. --help
. Ako ale vlastne zistíme, ktorý
prepínač používateľ použil? Najčastejšie sa používa funkcia
strcmp()
. Nájdeme ju v knižnici string
a ako už
názov napovedá, porovnáva dva reťazce znakov. Pokiaľ ju niekto nepoznáte,
môžete sa pre bližšie informácie pozrieť do manuálových stránok.
Základná funkčnosť je ale taká, že jej dáte dva stringy (alebo
presnejšie "pole znakov ukončené nulovým znakom") a ak sa tieto rovnajú,
vracia nulu.
if (!strcmp(argv[1], "-a") || !strcmp(argv[1], "--addition")) vysledek = a + b; else if (strcmp(argv[1], "-m") == 0 || strcmp(argv[1], "--multiplication") == 0) vysledek = a * b; else { fprintf(stderr, "Je nutne zvolit operaci!\n"); }
Týmto pomerne jednoduchým spôsobom sme zistili, ktorý prepínač
používateľ použil. Ako vidíte, použil som oba možné zápisy podmienky -
sú úplne ekvivalentné, takže je jedno, ktorý použijete - ja osobne
preferujem prvý spôsob, pretože mi pripadá prehľadnejšie .. V premenných
a
a b
sa už samozrejme nachádza prevedené
argumenty.
Kód pre výpis pomocníka bude obdobný. Musí ale byť pred overením správneho počtu argumentov, pretože by ním neprešiel (a teda by sa pomocník nikdy nevypísala). Čo nie je už tak zrejmé (a často sa zabúda) je nutnosť znovu overiť počet argumentov.
if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { fprintf(stdout, "Program se pouziva nasledovne: bla bla\n"); return 0; }
Celý program je v prílohe. Ukážka je ale čisto pre akademické účely - napríklad pre prevod argumentov by bolo vhodné použiť strtod a overovať, či sú to čísla.
Tip
Pre tých, čo nepoznajú príliš Linuxové systémy, mám jeden pekný trik. Ukážeme si to na programe, ktorý sme práve napísali ..
Fungovať bude s každým programom, ktorý vracia iba výsledok. Náš program sa bude správať presne tak. Výpis výsledku vyzerá nasledovne:
fprintf(stdout, "%d\n", vysledek);
Takto bude vyzerať bežné použitie nášho programu:
$ ./program -a 10 5 15 $
Môžeme ale použiť jednu peknú vlastnosť shellu ..
$ ./program -a 10 `./program -m 10 15` 160 $
Ak niečo uzavrieme do spätných apostrofov (AltGr + klávesa pod Esc),
shell to spustí prvýkrát a miesto týchto apostrofov vloží to, čo program
vypísal na stdout. V našom príklade teda shell najskôr spustil
./program -m 10 15
. Výsledok tohto volania je 150
.
Druhé spustenie nášho programu teda vyzeralo nasledovne:
./program -a 10 150
. Pekné, že?
Getopt
Čo sme si zatiaľ ukázali, je plne multiplatformový a malo by fungovať
prakticky všade. Na UNIXových operačných systémoch (spĺňajúca normu
POSIX) nájdeme funkciu getopt()
, ktorá nám môže uľahčiť
prácu (to platí predovšetkým pre programy s veľkým množstvom
prepínačov).
Nachádza sa v knižnici getopt
(alebo unistd
-
môže sa líšiť podľa systému), takže pre jej použitie musíme pripojiť
príslušný hlavičkový súbor:
#include <getopt.h>
Funkcia sa typicky používa v cykle a v kombinácii s príkazom
switch
. Funkciu odovzdáme argc
, argv
a
reťazec s možnými argumenty. Vie pracovať len s krátkymi argumenty.
while ((opt = getopt (argc, argv, "am")) != -1) { switch (opt) { case 'a': if (set) { fprintf(stderr, "Nelze pouzit -a a -m zaroven!\n"); return 1; } operace = ADD; break; case 'm': if (set) { fprintf(stderr, "Nelze pouzit -a a -m zaroven!\n"); return 1; } operace = MUL; break; } }
Funkcia getopt sa hodí skôr pre spracovanie argumentov ako takých, ako
prepínačov - v našom prípade som musel zabezpečiť výlučnosť pomocou
vlajky. Pokiaľ ale máme napríklad program, ktorý má množstvo argumentov,
ktoré napríklad nastavujú formát výstupu a možno ich kombinovať,
getopt
je ideálna funkcia.
Po prebehnutí tohto kódu vieme, či používateľ zadal -a
alebo -m
. Zaujímavá vlastnosť je, že nezáleží na poradí
argumentov - getopt
prechádza argv tak dlho, než ich nájde
všetky. Nájdené argumenty z argv
odstráni (čo sa nám bude
veľmi hodiť).
Náš program trochu zmeníme a doplníme. Teraz bude akceptovať ľubovoľný počet čísel na spracovanie a pokiaľ užívateľ nepoužije prepínač, predvolená operácia bude sčítanie.
if (!set) operace = ADD; if (operace == ADD) vysledek = 0; else if (operace == MUL) vysledek = 1; for (int i = optind; i < argc; i++) { if (operace == ADD) vysledek += atoi(argv[i]); else if (operace == MUL) vysledek *= atoi(argv[i]); }
For cyklus prejde zvyšné argumenty programu a vykoná príslušnú
operáciu. Z premenné optind
zistíme, kde začínajú
nespracované argumenty.
Celý program je opäť k dispozícii na stiahnutie.
Funkcia tiež podporuje spracovanie argumentov s parametrom. Ak napríklad
použijeme "a:"
, getopt
bude hľadať argument -a
nasledovaný parametrom. Toto je však GNU rozšírenie a bude teda fungovať
len na Linuxe.
$ ./program -a 15
Kód na spracovanie by vyzeral nasledovne:
case 'a': a_flag = true; moje_promenna = optarg; break;
Viac informácií nájdete v manuálových stránkach plus tu nájdete peknú ukážku is ošetrením chýbajúcich argumentov.
Getopt_long
Ako som povedal, getopt
neumožňuje spracovanie dlhých
argumentov (--argument). Preto tu máme pokročilejšie funkciu -
getopt_long
, ktorá zvláda oboje. Informácie o ňom si môžete
jednoducho nájsť v manuálových stránkach.
Nabudúce, v poslednej lekcii zaoberajúca sa filtre, Céčko a Linux - getopt_long a shell , sa pozrieme na spracovanie argumentov programu pomocou getopt_long. Na záver si ukážeme pár trikov v shellu.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 45x (1.1 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C