exec ?kapcsolók? arg ?arg ...?Az
exec
parancs egy parancs pipeline elemeinek
alfolyamatként történő végrehajtását kezdeményezi. Az
exec
argumentumai a pipeline részét képező unix
parancsokat és ezek kapcsolatát írják le. A pipeline megadásának
szintaxisa a unix-shellekben megszokotthoz hasonló.
A pipeline parancsai szavakból állnak, minden argumentum pontosan egy
szót ad meg. A parancs első szava a parancs neve. Ha a parancs neve
nem ad meg könyvtárat, vagyis a tilda-helyettesítés elvégzése után nem
tartalmaz '/' karaktert, az exec
a PATH
környezeti változóban felsorolt könyvtárakban elsőként megtalált
megfelelő nevű fájlt hajtja végre.
A pipeline parancs argumentumai pontosan a parancs nevét követő szavak
lesznek. Az exec
parancs ezeken semmilyen helyettesítést
nem végez. Ha mégis a shellben megszokott mintákkal szeretnénk
fájlneveket paraméterként átadni, a glob
parancsot kell
alkalmaznunk. Ekkor azonban nem elég a glob
parancsot
parancshelyettesítésbe bújtatni, mert eredménye az exec
és ezzel egyben a végrehajtandó parancs egyetlen argumentumává válna.
Egyik lehetséges kiút az eval
parancs használata.
A vezérlőargumentumok a pipeline parancsainak elválasztására és
inputjuk és outputjuk irányítására szolgálnak, ezek nem kerülnek a
végrehajtott programoknak átadásra. A paraméteres vezérlőargumentumok
paramétere képezheti a vezérlőargumentum részét (például
'>fájlnév
'), de állhat a következő argumentumban is
(például '> fájlnév
').
| | A pipeline két parancsát választja el. A megelőző parancs standard outputját a következő parancs standard inputjába irányítja. |
|& | A pipeline két parancsát választja el. A megelőző parancs standard és error outputját a következő parancs standard inputjába irányítja. Felülbírálja a '2>' és '>&' jellegű átirányításokat. |
<fájlnév | A megnevezett fájlt a pipeline első parancsának standard inputjába irányítja. |
<@azonosító | Az azonosítóval adott, olvasásra is megnyitott fájlt a pipeline első parancsának standard inputjába irányítja. |
<<érték | A megadott értéket a pipeline első parancsának standard inputjába irányítja. |
>fájlnév | A pipeline utolsó parancsának standard outputját a megadott fájlba irányítja, felülírva annak eredeti tartalmát. |
>@azonosító | A pipeline utolsó parancsának standard outputját az azonosítóval adott, írásra is megnyitott fájlba irányítja. |
>>fájlnév | A pipeline utolsó parancsának standard outputját a megadott fájl végéhez fűzi. |
2>fájlnév | A pipeline összes parancsának error outputját a megadott fájlba irányítja, felülírva annak eredeti tartalmát. |
2>@azonosító | A pipeline összes parancsának error outputját az azonosítóval adott, írásra is megnyitott fájlba irányítja. |
2>>fájlnév | A pipeline összes parancsának error outputját a megadott fájl végéhez fűzi. |
>&fájlnév | A pipeline utolsó parancsának standard outputját és összes parancsának error outputját a megadott fájlba irányítja, felülírva annak eredeti tartalmát. |
>&@azonosító | A pipeline utolsó parancsának standard outputját és összes parancsának error outputját az azonosítóval adott, írásra is megnyitott fájlba irányítja. |
>>&fájlnév | A pipeline utolsó parancsának standard outputját és összes parancsának error outputját a megadott fájl végéhez fűzi. |
& | Utolsó argumentumként adható meg, a pipeline aszinkron végrehajtását eredményezi. |
Ha a pipeline első parancsának standard inputját nem adtuk meg külön, akkor pipeline a Tcl interpretert végrehajtó folyamat standard inputjáról fog olvasni.
Szinkron végrehajtás esetén (nem '&' argumentummal zártuk le a
parancsot) az exec
parancs megvárja a pipeline
parancsainak befejeződését. Ha nem irányítottuk át, a pipeline utolsó
parancsának standard outputja lesz a parancs visszatérési értéke.
Az exec
parancs hibát jelez, ha a pipeline bármely eleme
abnormálisan fejeződik be. A hibaüzenet a pipeline outputjából és az
abnormális befejeződést leíró diagnózisból áll, az errorCode
változó az
utolsóként abnormálisan befejeződött paranccsal kapcsolatos
információkat tartalmaz. Az exec
parancs akkor is hibát
ad vissza, ha a pipeline bármely eleme error outputjára ír, és az
nincsen átirányítva. A hibaüzenet ekkor a standard outputot és a
diagnózist követően az error outputot is megadja.
Akár normálisan, akár hibával fejeződik be a szinkron
exec
parancs, törli az eredmény utolsó karakterét, ha az
újsor karakter.
Aszinkron végrehajtáskor az exec
nem várja meg a pipeline
végrehajtásának befejeződését, visszatérési értéke a pipeline
elemeinek folyamat-azonosító számait tartalmazó lista. Az aszinkron
futó pipeline parancsainak standard illetve error outputja - ha nem
irányítottuk át - a Tcl interpretert futtató folyamat standard illetve
error outputjára kerül.
Az exec
parancs két kapcsolót támogat:
-keepnewline
--
pid ?azonosító?A
pid
parancsot meghívhatjuk egy argumentummal vagy
argumentum nélkül. Az argumentum egy fájlazonosító lehet. Ha az
azonosító az open paranccsal megnyitott pipeline azonosítója, a
visszatérési érték a pipeline elemeinek folyamat-azonosító számát
tartalmazó lista. Ha az azonosító nem pipeline azonosítója, a
visszaadott lista üres.
Argumentum nélkül kiadva a pid
parancs a Tcl interpretert
végrehajtó folyamat-azonosító számát adja vissza.
exit ?kód?Az
exit
parancs a Tcl interpretert futtató folyamatot a
megadott visszatérési kóddal terminálja. A visszatérési kód
alapértelmezése 0.
Ez a fejezet a Tcl egyik fontos kiegészítéséről, a thread csomagról szól, amely lehetővé teszi szálak használatát egy Tcl szkriptben.
A szálkezelés rendkívül lényeges része egy programozási nyelvnek, hiszen enélkül bizonyos feladatokat csak nagyon körülményesen, vagy nem kielégítően lehet megoldani. Legegyszerűbb példa erre a grafikus kezelőfelület: egy-egy bonyolultabb folyamat elindításakor természetes elvárás, hogy amíg a gép dolgozik, a felhasználó tudjon dolgozni a programmal, vagy akár leállítsa az éppen futó folyamatot. Ehhez a programot párhuzamos folyamatokra kell bontani, amit szálakkal érhetünk el. Mivel a Tcl programozási nyelvet a Tk kiegészítés révén általában grafikus prototípusok készítésére használják, az ilyen problémák mindennaposak. Természetesen a helyzet megoldható az update paranccsal, illetve külső programok futtatásával és azok eredményeinek felhasználásával is, de egy ilyen gyakorta felmerülő kérdésre elegánsabb választ igényel a programozó világ - a szálakat.
A szálak használatára nincs felkészítve minden Tcl interpreter; fordításkor ezt általában egy parancssori argumentummal (vagy egy #define-al a Makefile-ban) lehet megkövetelni. Ha az interpreter vagy egy betöltött csomag nem szálbiztos, az könnyen a program hibás futásához illetve szegmentációs hibához vezethet.
A csomag letölthető a Tcl/Tk honlapjáról, betöltéséhez a forrásban a következő sornak kell szerepelnie:
package require Thread 2.1ahol a "2.1" természetesen a megkövetelt verziószámnak megfelelően változtatandó.
A Tcl szálmodellje szerint minden szálhoz tartozik legalább egy Tcl interpreter, de minden egyes interpretert csak egy-egy szál használhat. Ennek az osztott adatkezelés megvalósításánál van jelentősége - a szálak biztonságos használata érdekében az adatokat vagy a szálakban lokálisan kell definiálni, vagy mutexekkel kell megvédeni (bővebben erről később lesz szó, a szálbiztosság kapcsán). Következésképp a Tcl inkább az olyan egyszerűbb szálas feladatokra alkalmas, amelyek munkáltató - dolgozó alapúak, azaz van egy fő szál, ami több mellékfolyamatot elindít és kommunikál velük, illetve vár, hogy azok válaszoljanak. A szálak kommunikációja az interpreterek eseménysorán (event queue) keresztül történik, ez lehet szimmetrikus vagy aszimmetrikus. Szimmetrikus esetben a hívó fél megvárja, hogy válasz érkezzen, aszimmetrikus esetben rögtön továbbmegy. A 8.4-es verziótól kezdve már I/O csatornákat is át lehet adni szálak közt.
sajat_hibakezelo szal_id hiba_adatok
Ha egy Tcl szkriptben szálakat használunk, akkor minden, a szálakban használt utasításnak szálbiztosnak kell lennie, ellenkező esetben a program futása nem lesz biztonságos. A Tcl szkriptek prototípus-jellegük miatt legtöbbször C-ben írt saját könyvtárakat is betöltenek - ahhoz, hogy ezek szálbiztosak legyenek, használni kell a Thread kiegészítés C API-jait. Ehhez használhatóak mutexek, szinkronizálást elősegítő feltételváltozók (condition variables), valamint a szálakra nézve lokális adattárolás. Ezek a struktúrák mind automatikusan törlődnek, amikor a szál terminál, így nem kell velük külön törődni. Ha a lokális adatterületben kell felszabadítani memóriát, akkor ez egy szálspecifikus kilépéskezelővel (exit handler) tehető meg.
Egy régi forráskód szálbiztossá tételénél általában elég a globális adatstruktúrákra figyelni, és vagy mutexekkel korlátozni az ezekhez való hozzáférést, vagy a szálakra nézve lokális adatterületre átvinni őket.
TCL_DECLARE_MUTEX(mutexValtozo) (...) Tcl_MutexLock(&mutexValtozo); (műveletek a közös adatokkal...) Tcl_MutexUnlock(&mutexValtozo);
Tcl_MutexLock(&mutexValtozo); while (a_feltétel_hamis) { Tcl_ConditionWait(&feltetelValtozo, &mutexValtozo, NULL /* nincs időkorlát */); } Tcl_MutexUnlock(&mutexValtozo);
Tcl_MutexLock(&mutexValtozo); /* közös/osztott állapot beállítása */ Tcl_ConditionNotify(&feltetelValtozo); Tcl_MutexUnlock(&mutexValtozo);
Néhány szálbiztos könyvtár a Tcl-hez:
Egy egyszerű példa párhuzamosításra:
package require Thread 2.1 # # Új szál indítása. A visszatérési értéket (a szál azonosítóját) # berakjuk a threadID változóba, hogy később hivatkozni tudjunk rá. # set threadID [thread::create { # # Innentől kezdve a thread::wait utasításig (amivel a szál belép az # eseményciklusba) definiáljuk azokat az eljárásokat, amiket majd a # főprogram meg fog hívni. # # Ebben a példában egy egyszerű kis eljárás van csak, ami lefuttatja # a parancsot amit átadtak neki. Ez a parancs lehet akár egy hosszan # futó Tcl utasítás, vagy éppen egy külső program is. # # Az eredményt visszaküldi az ID azonosítóval meghatározott szálnak, # a globális printResult eljárás segítségével. # # A thread::wait utasításra azért van szükség, hogy a szál ne fejezze # be a működését és lépjen ki, hanem maradjon benn az eseményciklusban. # proc runCommand { ID command } { set result [eval $command] eval [subst {thread::send -async $ID \ {::printResult [list $result]}}] } thread::wait }] # # Ez az eljárás hívódik meg, amikor a szál befejezte a parancs futtatását. # proc printResult { result } { puts $result exit } # # Kiírja az aktuális időt és beütemezi önmagát egy másodperc múlvára. # proc passTheTime { } { puts [clock format [clock seconds]] after 1000 passTheTime } # # Ez itt egy általános utasítás külső program futtatására, csak az # exec utáni részt kell lecserélni valami másra. # set commandString "exec du -sk /usr/local" eval [subst {thread::send -async $threadID \ {runCommand 1 [list $commandString ]}}] # # Maga a program csak kiírogatja az időt és vár az örökkévalóságig. # Ezzel megmutatjuk, hogy miközben a szál dolgozik, a főprogram él. # passTheTime vwait forever