6. diel - Céčko a Linux - Filtre
V minulej lekcii, Céčko a Linux - Code :: Blocks druhýkrát , sme debuggovali v Code :: Blocks. Dnes sa budeme zaoberať tzv. Filtre. Filter je program, ktorý všetky vstupné informácie získa od užívateľa už pri svojom spustení. Jedná sa samozrejme o konzolové aplikácie a príkladom môže byť napríklad základné echo alebo napríklad cat. Vlastne sa dá povedať, že prevažná väčšina aplikácií pre terminál sú filtre.
Argumenty programu
Tieto aplikácie získavajú vstupné dáta pomocou takzvaných argumentov. Typický filter sa spúšťa nasledovne:
jméno_programu argument1 argument2 argument3 ...
Jednotlivé argumenty sú oddelené ľubovoľným počtom bielych znakov. Pri zadaní takého príkazu ho terminál spracuje a odovzdá spouštěnému programu.
int main(int argc, char **argv) // nebo char *argv[]
Takto zapísanou funkciu main () ste určite videli v nejakom tutoriálu alebo v cudzom kódu. Možno ste ale doteraz nevedeli, na čo je a váš kód bez problémov funguje aj keď vo funkcii main () žiadne argumenty nemáte. (Správne by však v takom prípade mal byť použitý void)
int main(void) // tímto říkáme, že funkce main() neočekává žádné parametry
Späť k argumentom programu. Ako iste mnohí tušíte, bude to mať niečo spoločné s argc a argv. Aby som nechodil zbytočne okolo horúcej kaše, tak v argc (argument count) nájdeme počet zadaných argumentov a v argv (argument values * touto skratkou si nie som úplne istý) ich hodnoty v podobe pole ukazovateľov na stringy. K samotným tvrdeniam môžeme pristupovať veľmi jednoducho.
Príklad
#include <stdio.h> int main(int argc, char **argv) { printf("%s %s %s\n", argv[0], argv[1], argv[2]); return 0; }
Skúsime program spustiť.
./priklad arg1 něco
Uvidíte, že výstup je vlastne to isté, čo sme zadali do terminálu. Z toho vyplýva zaujímavá vec - argument1 je uložený na indexe 1 av prvom nájdeme jeho názov (respektíve ako bol spustený - ak by sme ho spúšťali ako / priklady / priklad, bude tam presne toto ..)
Možné problémy
Možno niektoré z vás napadne, čo sa stane, keď takýto program spustíme bez parametrov. Určite si to vyskúšajte, ale každý bude mať pravdepodobne iné výsledky - správanie je totiž špecifikácií C nedefinované. V mojom prípade (Ubuntu 14.10 64bit) to funguje nasledovne. Najskôr sú všetky odovzdané argumenty (povedzme, že ich bude 6), v argv [7] je napísané "(null)" a nasleduje 70 rôznych premenných prostredia (napríklad v akom jazyku je prostredie (cz, en, ..), kde je má domovská zložka, aký používam terminál a pod.) Za nimi je ďalší reťazec "(null)" a pri pokuse o čítanie ďalej už "lezieme" mimo našu pamäť a jadro nás zabije.
Čo odovzdáva váš terminál môžete vyskúšať nasledujúcim programom:
int main(int argc, char **argv) { for (int i = 0; i < 200; i++) printf("%d: %s\n", i, argv[i]); return 0; }
Táto vlastnosť určite pôjde využiť, ale za cenu neprenosnosti nášho programu - odovzdávané argumenty sa budú v rôznych OS (a pravdepodobne aj verziách toho istého OS) líšiť. Preto máme k dispozícii argc. Náš jednoduchý program upravíme nasledovne:
for (int i = 0; i < argc; i++)
Teraz bude robiť to, že vypíše všetky argumenty, čo mu zadáme. To sa dá využiť mnohými zaujímavými spôsobmi. Napríklad keď budeme chcieť pracovať s dopredu neznámym počtom argumentov.
int sum = 0; for (int i = 1; i < argc; i++) sum += atoi(argv[i]); printf("Suma zadaných hodnot: %d\n", sum);
Tento krátky program jednoducho spočíta všetko, čo mu předhodíme v argumentu. Len by som podotkol, že nie sú ošetrené možné chybové stavy (užívateľ nezadá číslo), takže to berte len ako ukážku možností ... Ak by niekoho napadlo, že by sumu vracal v návratovom kóde programu (return sum;), tak to nepôjde, pretože návratový kód je len byte - teda do neho môžeme napchať maximálne 255. Testová otázka ... Čo sa stane, keď vrátime hodnotu vyššiu ako 255?
Čo sa týka maximálneho počtu argumentov, tak tým sa pravdepodobne nemusíte príliš zaoberať - väčšinou je to veľa (mňa sa ich tam vliezol milión a dva milióny už nie). Pričom priamo C to nijako obmedzené nemá. Obmedzenie je dané shellom, ktorý používate (ako maximálnym počtom argumentov, tak maximálnou dĺžkou príkazu). Ak by to niekoho viac zaujímalo, môže mrknúť napríklad na http://www.in-ulm.de/...ious/argmax/
Mimochodom, ak by ste chceli odovzdať programu väčšie množstvo argumentov z nejakého súboru, tak to možno urobiť napríklad takto:
./priklad `cat soubor`
Spätné apostrofy (pravý alt + klávesa pod Esc) spôsobí, že shell prvýkrát vykoná príkazy v nich a na ich miesto potom dá ich výstup ... Program cat vypíše obsah súborov, ktoré mu odovzdáte v argumentoch.
Práca s väčším množstvom dát
Všeobecne ale neodporúčam tento spôsob pre veľké množstvo dát - iné systémy to majú často obmedzené viac. Lepší spôsob je odovzdať v argumente súbor, prípadne vstupné dáta čítať zo štandardného vstupu (využitie presmerovanie).
int main(int argc, char **argv) { FILE *in; if (argc > 1) { if(!(in = fopen(argv[1], "r"))) { fprintf(stderr, "Soubor nelze otevřít!\n"); return 1; } } else in = stdin; ... fscanf(in, ...); char = fgetc(in); ... return 0;
Na pohľad možno desivý príklad, ale v skutočnosti nie je zložitý. Všetci určite poznáte funkcie ako getchar (), scanf (), printf () apod ... Najskôr tiež viete, že existujú ekvivalenty, ktoré robia to isté so súbormi (v skutočnosti sú to rovnaké funkcie a getchar (), printf (), ... sú len makrá, ktoré ich volajú a miesto súboru vloží stdin). Tieto funkcie vyžadujú ukazovateľ na súbor (v našom prípade 'in'). Ten bežne získame funkcií fopen (). Niektorí už ale možno neviete, že štandardný vstup (stdin), štandardný výstup (stdout) a chybový výstup (stderr) sú vlastne z pohľadu Linuxu tiež súbory.
A presne toho využíva príklad vyššie - pokiaľ užívateľ zadá názov súboru, tak ho otvorí a dáta sa budú načítať z neho. Ak nie, budú sa načítať zo štandardného vstupu (používateľ ich bude zadávať ručne - prípadne môže použiť presmerovanie). Pre všetky načítací funkcie potom musíme použiť verzia pre prácu so súbormi a odovzdať im 'in', v ktorom je buď ukazovateľ na súbor alebo na štandardný vstup. Kód som výrazne netestoval, ale zdá sa, že funguje (A myšlienka by určite fungovať mala.)
Nabudúce, v lekcii Céčko a Linux - Prepínače a getopt , sa pozrieme na použitie argumentov ako prepínačov (s rôznymi argumentmi sa bude program správať rôzne) a na argumenty s hodnotou. Nakoniec si ukážeme ako si uľahčiť život s pomocou getopt. Akékoľvek otázky, návrhy a pripomienky určite privítam