IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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 7 - Céčko a Linux - Přepínače a getopt

V minulé lekci, Céčko a Linux - Filtry, jsme si řekli něco o tom, jak funguje předávání argumentů ze shellu a jak je může náš program zpracovat.

Dnes si vyzkoušíme měnit chování programu pomocí přepínačů a napíšeme si jednoduchou demonstrační aplikaci.

Přepínače

Přepínač je argument programu, který typicky ovlivňuje (přepíná) jeho funkcionalitu. Například mějme program, který dostane tři argumenty. První bude určovat operaci, která bude provedena s následujícími dvěmy argumenty.

Přepínače obykle začínají znakem - pro zkrácenou podobu a dvěma znaky - pro nezkrácenou podobu. Můžeme si samozřejmě vymyslet jakýkoliv formát, ale tohle je takový standard a myslím, že není důvod ho nepoužívat. :)

Pro začátek si nadefinujeme dva přepínače:

  • sčítání: -a resp. --addition
  • násobení: -m resp. --multiplication

Program pak spustíme například takto:

$ ./program -a 10 5

Ukázka

Jako první věc musíme určitě ověřit, zda uživatel zadal správný počet argumentů. Kdybychom to neudělali, program by při nesprávném použití pracoval neočekávaně a uživatel by o tom nebyl informován.

if (argc != 4)
{
  fprintf(stderr, "Chybny pocet argumentu!\nNapovedu zobrazite pomoci prepinace -h\n");
  return 1;
}

Nezapomeňme, že první argument je název programu, takže musíme ověřovat na počet zadávaných argumentů + 1. Vidíme také, že by se asi hodil další přepínač pro zobrazení nápovědy. Použijeme standardní -h, resp. --help. Jak ale vlastně zjistíme, který přepínač uživatel použil? Nejčastěji se používá funkce strcmp(). Najdeme ji v knihovně string a jak už název napovídá, porovnává dva řetězce znaků. Pokud ji někdo neznáte, můžete se pro bližší informace podívat do manuálových stránek. Základní funkčnost je ale taková, že jí dáte dva stringy (nebo přesněji "pole znaků ukončené nulovým znakem") a pokud se tyto rovnají, vrací 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 poměrně jednoduchým způsobem jsme zjistili, který přepínač uživatel použil. Jak vidíte, použil jsem oba možné zápisy podmínky - jsou naprosto ekvivalentní, takže je jedno, který použijete - já osobně preferuji první způsob, protože mi připadá přehlednější.. V proměnných a a b se již samozřejmě nachází převedené argumenty.

Kód pro výpis nápovědy bude obdobný. Musí ale být před ověřením správného počtu argumentů, protože by jím neprošel (a tudíž by se nápověda nikdy nevypsala). Co není už tak zřejmé (a často se zapomíná) je nutnost znovu ověřit počet argumentů.

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 příloze. Ukázka je ale čistě pro akademické účely - například pro převod argumentů by bylo vhodné použít strtod a ověřovat, zda jsou to čísla.

Tip

Pro ty, co neznají příliš Linuxové systémy, mám jeden pěkný trik. Ukážeme si to na programu, který jsme právě napsali..

Fungovat bude s každým programem, který vrací pouze výsledek. Náš program se bude chovat přesně tak. Výpis výsledku vypadá následovně:

fprintf(stdout, "%d\n", vysledek);

Takhle bude vypadat běžné použití našeho programu:

$ ./program -a 10 5
15
$

Můžeme ale použít jednu pěknou vlastnost shellu..

$ ./program -a 10 `./program -m 10 15`
160
$

Pokud něco uzavřeme do zpětných apostrofů (AltGr + klávesa pod Esc), shell to spustí prvně a místo těchto apostrofů vloží to, co program vypsal na stdout. V našem příkladu tedy shell nejdříve spustil ./program -m 10 15. Výsledek tohoto volání je 150. Druhé spuštění našeho programu tedy vypadalo následovně: ./program -a 10 150. Pěkné, že?

getopt

Co jsme si zatím ukázali, je plně multiplatformní a mělo by fungovat prakticky všude. Na UNIXových operačních systémech (splňující normu POSIX) najdeme funkci getopt(), která nám může usnadnit práci (to platí především pro programy s velkým množstvím přepínačů).

Nachází se v knihovně getopt (nebo unistd - může se lišit dle systému), takže pro její použití musíme připojit příslušný hlavičkový soubor:

#include <getopt.h>

Funkce se typicky používá v cyklu a v kombinaci s příkazem switch. Funkci předáme argc, argv a řetězec s možnými argumenty. Umí pracovat jen s krátkými 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;
  }
}

Funkce getopt se hodí spíše pro zpracování argumentů jako takových, než přepínačů - v našem případě jsem musel zajistit výlučnost pomocí vlajky. Pokud ale máme například program, který má množství argumentů, které například nastavují formát výstupu a lze je kombinovat, getopt je ideální funkce.

Po proběhnutí tohoto kódu víme, jestli uživatel zadal -a nebo -m. Zajímavá vlastnost je, že nezáleží na pořadí argumentů - getopt prochází argv tak dlouho, než je najde všechny. Nalezené argumenty z argv odstraní (což se nám bude velice hodit).

Náš program trochu pozměníme. Nyní bude akceptovat libovolný počet čísel ke zpracování a pokud uživatel nepoužije přepínač, výchozí operace bude sčítání.

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 projde zbylé argumenty programu a provede příslušnou operaci. Z proměnné optind zjistíme, kde začínají nezpracované argumenty.

Celý program je opět k dispozici ke stažení.

Funkce také podporuje zpracování argumentů s parametrem. Pokud například použijeme "a:", getopt bude hledat argument -a následovaný parametrem. Toto je ovšem GNU rozšíření a bude tedy fungovat jen na Linuxu.

$ ./program -a 15

Kód na zpracování by vypadal následovně:

case 'a':
  a_flag = true;
  moje_promenna = optarg;
  break;

Více informací najdete v manuálových stránkách plus zde najdete pěknou ukázku i s ošetřením chybějících argumentů.

getopt_long

Jak jsem řekl, getopt neumožňuje zpracování dlouhých argumentů (--argument). Proto tu máme pokročilejší funkci - getopt_long, která zvládá oboje. Informace o něm si můžete jednoduše najít v manuálových stránkách.

Příště, v poslední lekci zabývající se filtry, Céčko a Linux - getopt_long a shell, se podíváme na zpracování argumentů programu pomocí getopt_long. Na závěr si ukážeme pár triků v shellu.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 43x (1.1 kB)
Aplikace je včetně zdrojových kódů v jazyce C

 

Předchozí článek
Céčko a Linux - Filtry
Všechny články v sekci
Linux a programování v jazyce C
Přeskočit článek
(nedoporučujeme)
Céčko a Linux - getopt_long a shell
Č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