6. díl - Céčko a Linux - Filtry

C++ Linux Céčko a Linux - Filtry

Dnes se budeme zabývat tzv. filtry. Filtr je program, který všechny vstupní informace získá od uživatele již při svém spuštění. Jedná se samozřejmě o konzolové aplikace a příkladem může být například základní echo nebo třeba cat. Vlastně se dá říci, že naprostá většina aplikací pro terminál jsou filtry.

Argumenty programu

Tyto aplikace získávají vstupní data pomocí takzvaných argumentů. Typický filtr se spouští takto:

jméno_programu argument1 argument2 argument3 ...

Jednotlivé argumenty jsou odděleny libovolným počtem bílých znaků. Při zadání takového příkazu ho terminál zpracuje a předá spouštěnému programu.

int main(int argc, char **argv)  // nebo char *argv[]

Takto zapsanou funkci main() jste určitě viděli v nějakém tutoriálu nebo v cizím kódu. Možná jste ale doposud nevěděli, k čemu je a váš kód bez problémů funguje i když ve funkci main() žádné argumenty nemáte. (Správně by ale v takovém případě měl být použit void)

int main(void)  // tímto říkáme, že funkce main() neočekává žádné parametry

Zpět k argumentům programu. Jak jistě mnozí tušíte, bude to mít něco společného s argc a argv. Abych nechodil zbytečně kolem horké kaše, tak v argc (argument count) nalezneme počet zadaných argumentů a v argv (argument values *touto zkratkou si nejsem úplně jistý) jejich hodnoty v podobě pole ukazatelů na stringy. K samotným argumentům můžeme přistupovat velmi jednoduše.

Příklad

#include <stdio.h>

int main(int argc, char **argv)
{
  printf("%s %s %s\n", argv[0], argv[1], argv[2]);
  return 0;
}

Zkusíme program spustit.

./priklad arg1 něco

Uvidíte, že výstup je vlastně to stejné, co jsme zadali do terminálu. Z toho plyne zajímavá věc – argument1 je uložen na indexu 1 a v prvním najdeme jeho název (respektive jak byl spuštěn – pokud bychom ho spouštěli jako /priklady/priklad, bude tam přesně tohle..)

Možné problémy

Možná některé z vás napadne, co se stane, když takový program spustíme bez parametrů. Určitě si to vyzkoušejte, ale každý bude mít pravděpodobně jiné výsledky – chování je totiž specifikací C nedefinované. V mém případě (Ubuntu 14.10 64bit) to funguje následovně. Nejdříve jsou všechny předané argumenty (řekněme, že jich bude 6), v argv[7] je napsané „(null)“ a následuje 70 různých proměnných prostředí (například v jakém jazyce je prostředí (cz, en,..), kde je má domovská složka, jaký používám terminál apod.) Za nimi je další řetězec „(null)“ a při pokusu o čtení dále již „lezeme“ mimo naši paměť a jádro nás zabije. ;)

Co předává váš terminál můžete vyzkoušet následujícím programem:

int main(int argc, char **argv)
{
  for (int i = 0; i < 200; i++)
    printf("%d: %s\n", i, argv[i]);
  return 0;
}

Tato vlastnost určitě půjde využít, ale za cenu nepřenositelnosti našeho programu – předávané argumenty se budou v různých OS (a nejspíše i verzích téhož OS) lišit. Proto máme k dispozici argc. Náš jednoduchý program upravíme následovně:

for (int i = 0; i < argc; i++)

Nyní bude dělat to, že vypíše všechny argumenty, co mu zadáme. To se dá využít mnoha zajímavými způsoby. Například když budeme chtít pracovat s dopředu neznámým počtem argumentů.

int sum = 0;
for (int i = 1; i < argc; i++)
  sum += atoi(argv[i]);

printf("Suma zadaných hodnot: %d\n", sum);

Tento krátký program jednoduše sečte vše, co mu předhodíme v argumentu. Jen bych podotkl, že nejsou ošetřeny možné chybové stavy (uživatel nezadá číslo), takže to berte jen jako ukázku možností... Pokud by někoho napadlo, že by sumu vracel v návratovém kódu programu (return sum;), tak to nepůjde, protože návratový kód je pouze byte – tudíž do něj můžeme nacpat maximálně 255. Testová otázka... Co se stane, když vrátíme hodnotu vyšší než 255? :)

Co se týče maximálního počtu argumentů, tak tím se nejspíše nemusíte příliš zabývat – většinou je to hodně (mě se jich tam vlezl milion a dva miliony už ne). Přičemž přímo C to nijak omezené nemá. Omezení je dáno shellem, který používáte (jak maximálním počtem argumentů, tak maximální délkou příkazu). Pokud by to někoho více zajímalo, může mrknout například na http://www.in-ulm.de/…ious/argmax/

Mimochodem, pokud byste chtěli předat programu větší množství argumentů z nějakého souboru, tak to lze udělat například takto:

./priklad `cat soubor`

Zpětné apostrofy (pravý alt + klávesa pod Esc) způsobí, že shell prvně vykoná příkazy v nich a na jejich místo pak dá jejich výstup... Program cat vypíše obsah souborů, které mu předáte v argumentech. ;)

Práce s větším množstvím dat

Obecně ale nedoporučuji tento způsob pro velké množství dat – jiné systémy to mají často omezené více. Lepší způsob je předat v argumentu soubor, případně vstupní data číst ze standardního vstupu (využití přesměrování).

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 pohled možná děsivý příklad, ale ve skutečnosti není složitý. Všichni určitě znáte funkce jako getchar(), scanf(), printf() apod... Nejspíše také víte, že existují ekvivalenty, které dělají to stejné se soubory (ve skutečnosti jsou to stejné funkce a getchar(), printf(),... jsou jen makra, které je volají a místo souboru vloží stdin). Tyto funkce vyžadují ukazatel na soubor (v našem případě 'in'). Ten běžně získáme funkcí fopen(). Někteří již ale možná nevíte, že standardní vstup (stdin), standardní výstup (stdout) a chybový výstup (stderr) jsou vlastně z pohledu Linuxu také soubory.

A přesně toho využívá příklad výše – pokud uživatel zadá název souboru, tak ho otevře a data se budou načítat z něj. Pokud ne, budou se načítat ze standardního vstupu (uživatel je bude zadávat ručně – případně může použít přesměrování). Pro všechny načítací funkce poté musíme použít verze pro práci se soubory a předat jim 'in', ve kterém je buď ukazatel na soubor nebo na standardní vstup. Kód jsem výrazně netestoval, ale zdá se, že funguje :) (A myšlenka by určitě fungovat měla.)

Příště se podíváme na použití argumentů jako přepínačů (s různými argumenty se bude program chovat různě) a na argumenty s hodnotou. Nakonec si ukážeme jak si usnadnit život s pomocí getopt. Jakékoliv dotazy, návrhy a připomínky určitě uvítám. :)


 

  Aktivity (1)

Článek pro vás napsal David Novák
Avatar
Autor v současné době studuje FIT VUT Brno a zajímá se především o nízkoúrovňové programování (C/C++, ASM) a návrh hardwaru (VHDL). Je zde také členem výzkumného týmu ANT@FIT (Accelerated Network Technologies).

Jak se ti líbí článek?
Celkem (2 hlasů) :
55555


 


Miniatura
Všechny články v sekci
Programování v jazyce C v Linuxu
Miniatura
Následující článek
Céčko a Linux - Přepínače a getopt

 

 

Komentáře
Zobrazit starší komentáře (5)

Avatar
Matej Bandik
Člen
Avatar
Matej Bandik:

Vo for( int name byť) musí sa deklarovať osobitne pretože to vypisuje chybu :)
ďakujem za článok ;)

Editováno 18.10.2015 22:16
 
Odpovědět 18.10.2015 22:15
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na Matej Bandik
David Novák:

Pochopil jsem správně, že for (int i = 0; ...) ti vyhazuje chybu?

Je to feature C99, takže musíš překládat takto: :)
gcc -std=c99

Odpovědět 18.10.2015 22:24
Chyba je mezi klávesnicí a židlí.
Avatar
Matej Bandik
Člen
Avatar
Matej Bandik:

Keď to zapíšem
int = i;
for (i=0;...)
tak to funguje :)

 
Odpovědět 18.10.2015 22:40
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na Matej Bandik
David Novák:

Samozřejmě - takhle se to dělalo v C dřív.. ;)

Když používáš standard C99 (z roku 1999), tak můžeš například deklarovat přímo ve for - tím se program o něco zpřehlední a navíc je pak proměnná platná pouze v rámci cyklu a po proběhnutí cyklu je zase ze zásobníku uvolněna.

Odpovědět 18.10.2015 23:32
Chyba je mezi klávesnicí a židlí.
Avatar
povijarrro
Člen
Avatar
povijarrro:

Ahoj. Dá zistiť pred kompiláciou a spustením programu kedy dôjde ku segmentation fault?
resp. aké najvyššie i nespôsobí pri príkaze argv[i] segmentation fault pri unix-like alebo program crash pri windowse?

 
Odpovědět 12. března 15:13
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na povijarrro
David Novák:

Před? To nevím.. Spíš asi ne. V C je z výkonnostních důvodů případná kontrola mezí zcela na programátorovi. Pro zjištění, na kterém příkazu to padá a jaký je v tu dobu obsah proměnných ale můžeš použít gdb (nebo jeho grafické nástavby v různých IDE).

Jaký je nejvyšší index pole víš jakožto programátor - pole máš buď staticky definované (kde přesně zadáš jeho velikost) a nebo ho alokuješ dynamicky při běhu - opět zadáváš jeho požadovanou velikost. Informaci o tom si můžeš uložit do nějaké proměnné nebo definovat jako konstantu u statických polí.. :)

Odpovědět 12. března 16:23
Chyba je mezi klávesnicí a židlí.
Avatar
povijarrro
Člen
Avatar
povijarrro:

to viem, že sa dá zistiť aký index sa používa. skôr som myslel ako zistím napríklad či si môžem dovoliť argv[40]
alebo nie. resp. či je alokovaná pamäť aspoň na null pre argv[i] alebo či už argv[i] segmentation fault spôsobí.
nemyslím si, že kompilovanie a debugovanie na každom jednom počítači (závisí to asi aj na konkrétnych shelloch a nastaveniach. nie?) kde sa má program spúšťat je možné.
alebo pristupovanie ku argv[i] s indexom viac ako argc-1 je práve prípad toho špecifického platform dependent kódu, ktorý musí byť kompilovaný na konkrétnom zariadení?

 
Odpovědět 13. března 8:55
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na povijarrro
David Novák:

Proč chceš přistupovat za tu hranici argc?

Co tam je nebo není,to už je implementačně závislé.. třeba bash tam nastrká různé env proměnné..

Odpovědět 13. března 15:02
Chyba je mezi klávesnicí a židlí.
Avatar
povijarrro
Člen
Avatar
Odpovídá na David Novák
povijarrro:

Ja nechcem nikde pristupovat pred precitanim tohto clanku som ani nevedel , ze sa to bez problemov skompiluje nie to ze este tam budu informacie o platforme pre to mi napadlo ako zistit naozajstny pocet argumentov

 
Odpovědět 13. března 17:15
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na povijarrro
David Novák:

Nevím, jestli přesně chápu, na co se ptáš.. Používej argc a máš to vyřešené :)

Odpovědět 13. března 18:41
Chyba je mezi klávesnicí a židlí.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 15. Zobrazit vše