Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 6 - Céčko a Linux - Filtry

V minulé lekci, Céčko a Linux - Code::Blocks podruhé, jsme debuggovali v Code::Blocks.

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 :)

Příště, v lekci Céčko a Linux - Přepínače a getopt, 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.


 

Předchozí článek
Céčko a Linux - Code::Blocks podruhé
Všechny články v sekci
Linux a programování v jazyce C
Přeskočit článek
(nedoporučujeme)
Céčko a Linux - Přepínače a getopt
Článek pro vás napsal David Novák
Avatar
Uživatelské hodnocení:
7 hlasů
Autor se zajímá především o nízkoúrovňové programování (C/C++, ASM) a návrh hardwaru (VHDL).
Aktivity