| ← přímý přístup | C/C++ | algoritmy třídění → |
V této kapitole se dozvíte, zda a jak je možné svým programem spustit program jiný. V souvislosti s tímto tématem by bylo možná vhodné pohovořit o vytváření dynamických knihoven (knihovna funkcí, které se může program za chodu rozhodnout načítat a používat), nebo třeba o spouštění podprocesů programu (spuštění několika „vláken“ programu, které běží současně a dělají různé vylomeniny), či o rozmnožení sebe sama (program spustí sám sebe jako další „vlákno“). Tyto věci jsou však více méně systémově závislé (spíše více) a z tohoto důvodu je nemám v úmyslu zde probírat. Bohužel, ani s přenositelností funkcí zde probíraných to není žádný med, to ostatně uvidíte sami.
Než začnu s výkladem, ukážu zde zdrojový kód programu mudrc, který budu v dalších programech spouštět.
1: /*------------------------------------------------*/ 2: /* mudrc.c */ 3: #include <stdio.h> 5: int main(int argc, char *argv[]) 6: { 7: int i; 8: printf("\n***************************\n"); 9: printf("* mudrc rika: *\n"); 10: switch (argc) { 11: case 1: 12: printf("* Kdo to rusi me kruhy? *\n"); 13: break; 14: default: 15: printf("* Argumenty: *\n"); 16: /* Prvni argument je nazev programu (preskocime ho). Muze to byt napr. 17: * "c:\mudrc.exe" nebo jen "mudrc". Zalezi to jednak na OS a v 18: * nasem pripade take na funkci, ktera tento program bude volat.*/ 19: for (i = 1; i < argc; i++) { 20: printf("* %2i) %-19s *\n", i, argv[i]); 21: } 22: break; 23: } 24: printf("***************************\n\n"); 25: return 0; 26: } 28: /*------------------------------------------------*/
Pokud programu mudrc nepředáte žádné argumenty, vypíše jen hlášku Kdo to rusi me kruhy?, jinak vypíše zadané argumenty.
Záměrně nevypisuji první argument (tedy argv[0]), který obsahuje jméno spouštěného programu. To totiž může být jednak "c:\mudrc.exe", nebo "mudrc.exe" nebo jen "mudrc". První argument může být plné jméno programu, nebo jen jméno (případně s relativní cestou (..\mudrc.exe atp.), nebo dokonce jméno bez přípony. To se v různých systémech liší. Vy si samozřejmě můžete program upravit tak, aby vypisoval i argv[0].
Funkce system() je jako jediná z funkcí zde zmiňovaných definována v normě ANSI C. S jejím použitím máte tedy zaručenu největší kompatibilitu. Funkce system(), jak už víte, spouští jiný program. Podívejme se na to, jakým způsobem to dělá.
int system(const char * string);
Funkce má pouze jediný argument (textový řetězec), kterým je příkaz, který chcete spustit. Záměrně říkám příkaz a ne program. Funkce system() totiž spustí příkazový interpret a tomu pak tento příkaz předá. Poté, co se příkazový interpret spustí, vykoná příkaz a ukončí se, vrátí řízení vašemu programu. Příkazem může být cokoliv, co můžete zadat na příkazovou řádku.
To, jaký příkazový interpret se spustí je ovlivněno tím, kde program běží. To je také funkci system() hodně vyčítáno. V DOSu (Windows) půjde nejpravděpodobněji o command.com MS-DOSu (ale existují i jiné DOS-systémy). V Linuxu může jít o bash, tcsh, csh atp. Tím je také dáno, jaké systémové proměnné příkazový interpret bude obsahovat (například PATH). Při každém spuštění funkce system() se spustí nový příkazový interpret. Pokud tedy v jednom volání funkce system() nastavíte proměnnou, v dalším volání funkce system() toto nastavení již nebude platné.
Další nevýhodou je náročnost na prostředky. Kromě spouštěného programu spouštíte ještě příkazový interpret. To však na dnešních nadupaných počítačích nemá téměř význam (nechcete-li z nějakého důvodu spouštět malý prográmek v cyklu milionkrát za sebou – to už by bylo citelně pomalé).
Jak tedy spustit správně příkaz? Pokud jde o příkazy samotného interpretu, je to jednoduché. Stačí zadat jejich jméno (a za ním parametry). Například command.com obsahuje příkaz pause, který způsobí pozastavení vykonávání příkazového interpretu (a ve svém důsledku pozastavení programu, který tento příkaz pomocí system() spustil).
system("pause");
Dalším příkladem může být příkaz DIR, u kterého budeme chtít vypsat jen textové soubory.
system("dir *.txt");
Můžete spustit opravdu cokoliv, co v příkazovém interpretu. Takže například i přesměrování výstupu do souboru.
system("dir *.txt > vystup.log");
Se spouštěním programů je to, oproti příkazům příkazového řádku, už o trošku horší. Především musíte zajistit, aby příkazový interpret věděl, kde se program daného jména nachází. Nejjednodušším řešením je uvést plné jméno programu. Jinak příkazový interpret hledá například v aktuálním adresáři, nebo adresářích určených v systémové proměnné PATH (v DOSu inicializaci proměnné PATH najdete v souboru c:\autoexec.bat).
Návratovou hodnotou funkce system() je 0 v případě úspěchu, jinak -1. Další informace o návratovém kódu a funkci system() najdete v kapitole o knihovně <stdlib.h>, kde je funkce system() deklarována. Najdete tam i příklad použití.
Tady si ukážeme příklad, jak spustit program mudrc. Předpokládám, že program mudrc je ve stejném adresáři, jako program system2 a že v proměnné PATH je uveden i aktuální adresář (tj. tečka).
1: /*------------------------------------------------*/ 2: /* system2.c */ 3: #include <stdlib.h> 4: #include <stdio.h> 6: #ifdef unix 7: #include <sys/wait.h> 8: #endif 10: int main(void) 11: { 12: printf("system2: system(\"mudrc\");\n"); 13: system("mudrc"); 14: printf("system2: system(\"mudrc Jak se vede?\");\n"); 15: system("mudrc Jak se vede?"); 16: printf("system2: Konec programu.\n"); 18: return 0; 19: } 21: /*------------------------------------------------*/
Výstup z programu:
system2: system("mudrc");
***************************
* mudrc rika: *
* Kdo to rusi me kruhy? *
***************************
system2: system("mudrc Jak se vede?");
***************************
* mudrc rika: *
* Argumenty: *
* 1) Jak *
* 2) se *
* 3) vede? *
***************************
system2: Konec programu.
Při spuštění programu system2 a přesměrování jeho výstupu do souboru (dělal jsem to v Linuxu) se stala zvláštní věc. V souboru byly na začátku výstupy ze spuštěných programů mudrc a až na konci výstupy (z funkcí printf()) programu system2. Došlo k tomu zřejmě tím, že spuštěný program mudrc měl ve chvíli svého vykonávání vyšší prioritu než program system2 a tak se mu podařilo výstup z programu system2 předběhnout. Člověk někdy žasne, na co si při programování musí dávat pozor. Toto podivné chování můžete brát jako další nevýhodu funkce system() oproti ostatním zde probíraným funkcím. Jenom pro zajímavost ukážu, jak vypadal výstup přesměrovaný do souboru následujícím příkazem:
system2 > soubor.txt
***************************
* mudrc rika: *
* Kdo to rusi me kruhy? *
***************************
***************************
* mudrc rika: *
* Argumenty: *
* 1) Jak *
* 2) se *
* 3) vede? *
***************************
system2: system("mudrc");
system2: system("mudrc Jak se vede?");
system2: Konec programu.
Poznámka: Zabránit tomuto podivnému chování lze použitím funkce fflush(stdout).
Funkce exec() nahradí aktuální proces novým (blbý, co?). Načte zadaný program do paměti a ten, který funkci exec() spustil skončí (v případě úspěchu). Pokud se program funkcí exec() nepodaří spustit, aktuální proces poběží dál, funkce vrátí -1 a globální proměnná errno se nastaví na hodnotu chyby. Funkce exec() nespouští příkazový interpret (pokud to není přímo program, který chceme spustit :-), ale jen daný program (rychlé a účinné).
Existuje celá rodina funkcí exec(), které si postupně probereme. Funkce exec* nejsou součástí normy ANSI C. Z toho důvodu jejich deklaraci můžete v různých systémech najít v různých hlavičkových souborech (<unistd.h>, <process.h>, <stdlib.h>), nebo je nenajdete vůbec.
int execl (const char *path, const char *arg, ...); int execlp (const char *file, const char *arg, ...); int execle (const char *path, const char *arg, ..., char *const envp[]); int execv (const char *path, char *const argv[]); int execvp (const char *file, char *const argv[]);
Všechny předchozí funkce jsou (většinou) implementovány pomocí následující funkce:
int execve (const char *file, char *const argv[], char *const envp[]);
U všech funkcí je prvním argumentem název programu, který chceme
spustit. Funkce execlp() a
execvp() se liší tím, že při
hledání programu prohledávají adresáře určené systémovou proměnnou
PATH (pokud není program zadán s absolutní nebo relativní cestou).
Dalšími argumenty funkcí exec jsou argumenty předávané spouštěnému
programu. Pamatujte si, že první předávaný argument by mělo být
jméno spouštěného programu a poslední argument musí být ukazatel
NULL. Funkce execle() a
execve() umožňují nastavit
nové systémové proměnné pro spouštěný program (pomocí argumentů
envp).
Rozdíl mezi funkcemi které mají v názvu l
a těmi, které mají v názvu v je jen ve způsobu
předávání argumentů.
Příklad:
1: /*------------------------------------------------*/ 2: /* exec1.c */ 4: #include <stdlib.h> 5: #include <stdio.h> 6: #ifndef unix 7: #include <process.h> 8: #else 9: #include <unistd.h> 10: #endif 11: #include <errno.h> 13: int main(void) 14: { 15: char *argv[] = { "mudrc", "prvni", "druhy", "treti", NULL }; 16: char *env[] = { "PATH=.", NULL }; 17: int navrat; 19: printf("exec1: execl(\"nesmysl\",\"nesmysl\",\"argument1\",NULL);\n"); 21: navrat = execl("nesmysl", "nesmysl", "argument1", NULL); 22: /* kdyby nesmysl existoval, nasledujici kod by jiz neprobehl */ 23: fprintf(stderr, "Navrat execl = %2i\n", navrat); 24: perror("CHYBA EXECL"); 26: printf("exec1: execve(\"mudrc\", argv, env);\n"); 27: execve("mudrc", argv, env); 28: /* pokud se podari spustit program mudrc, nasledujici kod se jiz neprovede */ 29: fprintf(stderr, "Navrat execl = %2i\n", navrat); 30: perror("CHYBA EXECVE"); 32: return 0; 33: } 35: /*------------------------------------------------*/
Výstup z programu:
exec1: execl("nesmysl","nesmysl","argument1",NULL);
Navrat execl = -1
CHYBA EXECL: No such file or directory
exec1: execve("mudrc", argv, env);
***************************
* mudrc rika: *
* Argumenty: *
* 1) prvni *
* 2) druhy *
* 3) treti *
***************************
Pokud vám něco z příkladu není jasné, projděte si nápovědu (nebo manuálové stránky). Mimochodem, pokud jste se až dosud během výuky nepodívali do nápovědy, asi z vás moc dobrý programátor nebude.
Rodina funkcí spawn* se chová stejně jako exec* s tím rozdílem, že aktuální proces nenahradí a po svém skončení se pokračuje v jeho vykonávání (myslím ve vykonávání procesu, který funkci spawn* spustil). Dostupnost těchto funkcí je ještě menší, než funkcí exec*. V Linuxu to prostě nepřeložíte.
V Linuxu máte k dispozici funkce fork() a vfork(), jejichž použití je však daleko náročnější.
Příklad:
1: /*------------------------------------------------*/ 2: /* spawn1.c */ 3: #include <stdlib.h> 4: #include <stdio.h> 5: #include <process.h> 6: #include <errno.h> 8: int main(void) 9: { 11: printf 12: ("spawn1: spawnl(\"mudrc\",\"mudrc\",\"prvni\",\"druhy\",\"treti\",NULL);\n"); 13: if (spawnl(P_WAIT, "mudrc", "mudrc", "prvni", "druhy", "treti", NULL) 14: == -1) 15: perror("CHYBA SPAWNL"); 17: printf("spawn1: konec.\n"); 18: return 0; 19: } 21: /*------------------------------------------------*/
Výstup z programu:
spawn1: spawnl("mudrc","mudrc","prvni","druhy","treti",NULL);
***************************
* mudrc rika: *
* Argumenty: *
* 1) prvni *
* 2) druhy *
* 3) treti *
***************************
spawn1: konec.
| ← přímý přístup | C/C++ | algoritmy třídění → |
