Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.

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

 

Predchádzajúci článok
Céčko a Linux - Filtre
Všetky články v sekcii
Linux a programovanie v jazyku C
Preskočiť článok
(neodporúčame)
Céčko a Linux - getopt_long a shell
Článok pre vás napísal David Novák
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se zajímá především o nízkoúrovňové programování (C/C++, ASM) a návrh hardwaru (VHDL).
Aktivity