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 46x (1.1 kB)
Aplikace je včetně zdrojových kódů v jazyce C