september 30, 2015

Pakkfailide feilimisest




Eestikeelses arvutiteaduses on pätukate, bät- (.bat / .cmd ) failide kohta lõpuks juurdunud omakeelne sõna pakkfail.
Las tal siis edasi tähistada neid veidraid natukene nagu programmeerimiskeelt meenutavaid .bat või .cmd laiendiga faile.

Märkus:
Kuigi ma kasutan siin enamasti mõisteid .bat keel / .bat failid, tuleks praegu kasutada pakkfailide tegemisel eranditult .cmd laiendit. BAT tuleneb sõnast batch - pakk, ports, ahjutäis...ja sellest siis selline tõlketermin - pakkfail.
Tõsi, .bat laiendiga failid käivituvad sama edukalt, kui .cmd laiendiga, nii et ikkagi maitse asi.

Päris mööda pakkfailidest ei pääse, kui on vaja tegelda W administreerimisega. Tänapäeval ei jõua W administraator igale poole ja hoolimata ühe või teise vahendi headustest või halbustest on vajalik kasutusele võtta iga asi, mis tööd hõlbustaks.
Pakkfailide ainuke hea omadus on see, et need jooksevad enam-vähem kõikides windowsi perekonna masinates ja vahel on see administraatori jaoks põhiline kriteerium. Ülejäänud vahendite kohta seda alati öelda ei saa.

Seetõttu ei pruugi muud üle jääda, kui veidrused .bat keeles välja kannatada ja ära õppida.
Programmeerimist alles õppijate jaoks aga on pakkfailide "võlu" pigem selles, et see “keel” või skriptimisvahend on skandaalselt põrunud, kohati lausa jabur ja selles mõttes õppuritele erakordselt sobiv õppematerjal, et näidata, kuidas ehk ei tohiks mõnda programmeerimiskeelt üles ehitada.
Üldiselt bash programmerimise studeerimise järel võib öelda ka Bourne again kesta kohta just mitte kõige paremaid sõnu, aga piisab vaid, kui tulla ja hakata nikerdama skripte pisipehmete akende jaoks - kohe hakkad taga igatsema vana head Unix-it.
Windows-i arendajatele on olnud ka paremaid aegu. Ühel ilusal ajal mõeldi, et Windowsi kesta programeerimiskeeleks võiks olla VBS ja see hakkas levima ja ongi veel õnneks üsna levinud siin ja seal. Või siis JavaScripti MS versioon JScript.
Ja siis tuli vist Balmeril pähe mõtte aretada välja täiesti oma eriline keel nimega Power Shell ja täpselt sama ämber algas otsast peale ja sellele ei paistagi MS maailmas lõppu tulevat...

Edsger Dijkstra on Coboli kohta öelnud et selle kasutamine invaliidistab mõistuse ja seetõttu tuleks selle õpetamist käsitleda kriminaalkuritööna.  <16>
Mina päris nii ei julgeks väita. Mina peaksin siis ka ju olema vaimuinvaliid, pomisen omaette ...
...andmebaaside nikerdamine peaks olema vastav tunnistuse saamisel täiesti piisav kriteerium.

Endasuguste kaitseks julgen laliseda, et tänapäeva koodinikerdaja peab olema tuttav mitmesugust laadi asjandustega ja kasutajad ei küsi, kas ja missugune töövahend parajasti kasutusel on, tema tahab näha töötavat arvutit, millega teha tööd.
Kui Dijkstra loogika kehtiks, tuleks kõik IT-d õpetavad õppejõud viivitamatult vanglasse toppida, nii 12. aastaks ja nende aretatud vaimuinvaliide kibekiiresti ravima hakata. Ja kuna neid progejate kambas on enamik, siis mõneks ajaks peatada tsivilisatsiooni toimimine, seniks, kuni psühhiaatrid oma tööga maha saavad, kui üldse saavad.
Ei ole lihtsalt võimalik, et oma elutegevuse keskel programmeerijal ei tuleks tegemist teha eriliselt jaburate programmeerimiskeelte veel jaburamate konstruktsioonidega...
Aga tuleme nüüd siis tagasi vaimuinvaliidide erilise koondumiskoha - MS .bat failide programmeerimise juurde.

I Hallo, Maailm - kahjuks koos lisanditega ...

Otsige üles oma op. süsteemi versioonile vastavalt run rida ja kirjutage sinna cmd. Kui midagi ära ka tahate teha arvuti juures, käivitage cmd aken administraatori rezhiimis. Kirjutage sinna laiust juurde, mode 100,100 avab võimaluse 100 sümbolit ritta panna (niinimetatud prompt võtab sellestki palju ära, aga tühja sellest).
Esimene asi, mida vaimuinvaliidiks saamiseks on vaja õpetada, on programm nimega

Hallo maailm.

See tüütus sai alguse muidugi Kerninghami ja Ritchie C vanast testamendist <15> ja kestab tänase päevani. Umbes nagu Piibli esimene rida kristlastele:
Alguses lõi Jumal taeva ja maa

Programmeerimise ülekaalukalt popima lause “Hallo maailm” väljastamiseks on igal pool oma võlusõna. W käsuaknas on selleks sama sõna, mis Unix-is: echo.
> echo “Hallo maailm!”
Ja saate .... saate mille
Saate

Hallo maailm!”

Oot, oot... aga miks jutumärgid kaasa tulid?
Igal pool mujal jutumärke ei panda kaasa, see on pakend, see eraldab echo järel tuleva käsurea argumendi alguse ja lõpu ülejäänud reast ja sageli on see hädavajalik, sest mingile programmile on vaja ühes tükis, ühe argumendina mingid andmed ette sööta, ja selle järel tuleb järgmine argument, või andmejupp, mis peab olema eraldatud eelmisest üheselt. Lisatud jutumärgid küll päästavad nende sisu mõningate erisümbolite erikohtlemise eest, aga mitte kõigi. Ühekordsed jutumärgid on
.bat-s täiesti kasutud isegi selles mõttes.
Siit algab suur jama, sest .bat programmide argumendid on vaja igaks juhuks jutumärkidest vabastada, mõnikord peavad need jälle sõnel - stringil ümber olema ja kui neid ülearu saab, siis ei saa programm mitte millestki aru.

II Muutujatest, mis ei taha muutuda

Igas normaalsusele pürgivas programmeerimise keeles on olemas muutujad. Isegi pisipehme bat keeles.
Lisaks kõigele on igas operatsioonisüsteemis olemas keskkonnamuutujad, mis ütlevad käivituse järgselt ja logimise järgselt süsteemile olulisi asju, näiteks ka selle, kes on sisse loginud ja millised on tema eelistused (kasutaja keskkonnamuutujad), ning süsteemimuutujad, mis kehtivad kõigile.
Nende kõigi (keskkonna)muutujate vaatamiseks, aga muutmiseks ainult (rõhutatult) selles aknas, kus olete käivitanud W cmd.exe, kas tavakasutajana, või administraatorina, on olemas käsk SET.

Väikene lisamure: ( ärge muretsege, see ongi nii W keskkondades - väikestest probleemidest saavad kiiresti suured !)
Kui te tahate, et nende muutujate väärtused jõuaksid ka järgmistesse cmd akendesse või saavad teie töötavate programmide sisemiseks teadmiseks, kasutage (täpsemalt, ärge veel kasutage) käsku SETX. Seda ei ole muuseas kõikides windowsi versioonides ...
SETX kahjuks nõuab oma muudatuste elluviimiseks rebooti.
GUI-d kasutades (täpsemad juhendid vt. google, see ei mahu selle kirjutise raamidesse) saate palju parema tulemuse, muutuja uus väärtus jõuab kohe vajaminevate programmide teadvusse.
Õnneks enamik arendajaid ei kasuta programmide töökeskkonna sättimiseks keskkonnamuutujaid, vaid programmid loevad omale vajaliku info otse registrist. Õnneks on registrisse kirjutamise ja sealt lugemise vahendid .bat keeles olemas.

Kõikide käibivate muutujate vaatamiseks tuleb tippida SET ilma lisadeta.
Nende muutujate vaatlemiseks, mis algavad P tähega, tippige
SET P
(näiteks PATH, mis alati peab olemas olema).
Muutuja hei sättimiseks kirjutage SET hei=hai
Täpsemalt siiski, ärge nii kirjutage.
kirjutage SET "hei=hai" et täpselt ja konkreetselt ära ütelda, mida te muutuja hei sisse tahate panna. Varsti näete kohe ühe näite varal, miks selline kindlustus alati hea on.
Ja siin lõpujutumärk õnneks ei lähe tõesti hei väärtuse sisusse, nagu võiks karta W bat-i kombeid juba natuke teades!

Ja ühe muutuja väärtuse väljastamiseks (vaatamine käibib rohkem programmi silumisel) kasutage
(või täpsemini, sellest hiljem, praegu kasutage, hiljem ärge kasutage selles vormis)
käsku
> echo %hei%
Muutuja nimi tuleb panna protsendimärkide vahele.
Muutujate interpreteerimisest ei päästa mitte mingisugused jutumärgid ... perlis bashis on need võimalused olemas.
Muutuja hei olemasolu tühistamiseks kirjutage
set "hei="
> set hei
ütleb nüüd, et
Environment variable hei not defined

Kuidas mitut käsku ritta panna

Et kannatamatut lugejat viia kohe II põhiteema juurde - näha oma silmaga ära, kuidas on võimalik, et muutuja ei muutu - võimalik ainult MS-s(!) - , õpime mitut käsku paigutama ühte ritta.
Selleks on ampersand, & .
Normaalsetes, mitte ajuinvaliididele määratud keeltes on selleks mõni üldkasutatavatest kirjavahemärkidest - enamasti semikoolon ;
Windows-is on selleks ampersand ja me näeme kohe, milleks see "hea" on.
Võib-olla tekib teil soov (enne seda, kui olete ampersandi eriomadustest teadlikuks saanud)
tavalise Hallo maailm väljastada Hallo & Hello, maailm!
Saate kurva feili:
Programm teatab teile, et ...

Hallo
'Hello' is not recognized as an internal or external command,
operable program or batch file.

Põhjuseks muidugi see ampersand - cmd tahab nüüd pärast echo Hallo täitmist täita asuda järgmise "käsu", Hello täitmise juurde (märgime, et "," ning "maailm!" lähevad selle käsu parameetriteks).
Kui aga panete "Hallo & Hello, maailm!" jutumärkidesse, saate tasuta kaasväljaandena kaasa jutumärgid ...
ERIMÄRKIDEST pääsemiseks tavaliselt piisab paosümboli teadmisest, kuigi ka mujal (bash) on nendega tegemist.
Aga WINDOWS-s on siin asi täiesti E r i l i n e
Erimärkidest lähemalt III teema all, kus harjutame alguses tühisena näiva probleemi, TÜHJA rea tekitamise lahendamist...

Kuidas muutuja ei muutu

Ok, paneme 2 käsku ühte ritta. Igaks juhuks tehke enne Set "hei=", et saada ühesugused tulemid, mine tea, mis väärtusi te hei-le olete harjutamise käigus vahepeal tekitanud.
Siis tippige
set hei=hai & echo %hei%
mis bat keeles ütleb, et paneme muutuja hei väärtuse väärtuseks hai ja seejärel väljastame ekraanile selle muutuja väärtuse.
Mitte just eriti intelligentne jupp koodi, aga midagi sarnast võib ju pähe tulla, panna ühele reale muutuja muutmise käsk ja tulemi(te) väljastamine.
Saate
%hei%
- DOS-i omapäraselt humoorikas viis ütelda echo peale, et muutujat hei ei ole olemas.
Tehke sama asja teist korda - muutuja hei on nüüd tõesti olemas - ja saate
hai
nagu tahtsitegi.
See ei ole uni ja see ei ole viga, see ongi bat keel, batman...!

See on ka see põhjus, miks peaks iga õppur, kes kunagi tahab välja mõelda päris oma programeerimiskeelt (aga kes ei tahaks, iseasi, kas õnnestub!) peaks teadma bat keelt!

Batmannide keele väljamõtlejad arvasid algul õigusega, et enne käsu täitmist tuleks üle vaadata, mis parameetreid käsk sisaldab. Seetõttu käib bat interpretaator enne käsu täitmist käsu üle, asendades muuhulgas %xxx% muutuja xxx väärtusega.
Näiteks kirjutatakse %hei% asemele selle muutuja väärtus enne %hei% -d sisaldava käsu täitmist.
Sellist asja nimetatakse muutujalaienduseks (variable expansion). Seda tehakse kohe enne käsku, sellest termin immediate variable expansion.

Siis tuli arendajatele külla Balmer ja ütles: hei, poisid, täna teeme nii, et mitu käsku saab panna ühele reale või käsud võib panna kokku ühte plokki. Et bashis on ka midagi sellist ja meie peame ka nii tegema...
See vend aga, kes immediate variable expansioniga tegeles, arvas, et tema ei viitsi küll hakata oma programmijuppi täiendama - lihtsalt teeme nüüd selle variable expansioni enne käsuplokki ära.
Ja edasine, mis selles käsuplokis juhtub, ei ole minu asi.
Käsuplokkide sekka sattusid ka kõik käsud, mis olid ühele reale aetud & -ga ühendatult. Hiljem näeme, et neid saab ühendada veel && -ga ja koguni || -ga, igal pool sees sama jama - muutuja käsuploki sees ei muutu mitte!
Nii et te võite käsuplokis muuta muutujat palju tahate, ta ei muutu mitte enne, kui see käsuplokk läbi saab ja jõussegi jääb vaid viimane nendest muutustest - viimane omistus hei=%xxxx% ja ülejäänud muudatused lendavad looja karja ...
Käsuplokke saate teha ka ( ) -ga.
sama asi, mis sai tehtud & -ga, on ( ) -ga eraldatud käsuplokis selline, tehtud cmd käsureal (enne set "hei=" jälle).

C:\bat>( set hei=hai
More? echo %hei%
More? )
%hei%

Siin on asi pisut selgem - bat interpreet ei varja, et ta ootab veel sisendit, ja käsuploki lõpetamise sulgu ja siis täidab selle, enne muidugi asendades %hei% tema mitteolemasoleva väärtusega....
.bat faili enda sees selliseid hoiatavaid More? –sid ei tule.

Sama nali juhtub ka nn. for tsüklis, millest lähemalt hiljem:
Aga toome ära selle programmeerimisalase humoreski ja saadava vastuse:

@echo off

set COUNT=0

for %%v in (1 2 3 4) do (

set /A COUNT=%COUNT%+ 1

echo %COUNT%

)
echo Ja count on nüüd: %count%

Saate ekraanile sellise tulemuse:
0 
0 
0 
0 
Ja count on nüüd: 1 

Count väärtuseks - neli korda on teda suurendatud, alati 0-st 1-ni, tuleb ikka 1
Võti selle jama mõistmiseks: for tsüklis do märgi järel tulevat käsitletakse ÜHE käsuna...koos FOR-ga.
Kahjuks kuulub for tsükkel asjade hulka, mida batman peab oskama, teisi tsüklikonstruktsioone bat keeles lihtsalt ei ole, tõsi,
on veel goto :label -i abiga tehtav tsükkel - tegelikult kõige kindlam ja parem tsüklikonstruktsioon bat keeles, sest ei toimi ootamatult.
Mittepühendatutele selgituseks - set /A tähendab, et nüüd tehakse aritmeetikat, muidu set sellist asja lihtsalt ei oska.
%%v on for-i tsüklimuutuja, mis käib läbi väärtused 1,2,3,4 ja praegu mujal tsüklis ei kajastugi. Millepärast tsüklimuutujad, täiesti erilised objektid BAT-s, on kahe %% märgiga ja miks nad näiteks cmd.exe käsuakna realt etteantuna peavad olema ühe % märgiga ja miks neid mujal kasutada ei saa - seda kõike küsige Balmerilt. Tema teab, miks sellised imelikud otsused vastu on võetud for tsükli kavandamisel. Aga selleni veel jõuame...
@echo off lülitab välja tüütu kaja ja @ märk ütleb echole ise, et ära iseennast ka kajasta ...
(proovige ilma ka, siis näete vahet).
Näide, kuidas mitte parandada programmeerimiskeele vigu: SETLOCAL

Isegi MS iselaadse huumorimeelega juba harjunud inimeste seas tekitas selline "muutujate" käitumine muidugi palju nurinat. Oli veel nurinat sellegi üle, et kõik muutujad olid globaalsed - nähtavad tervele programmile või programmide grupile ja iga programmijupp pääses teise programmijupi muutujatele vabalt ligi.
Ja lõpuks ei olnud .bat keeles algul peaaegu üldse mitte tsükleid, v.a. :label ja goto :label abiga tehtavad tsüklid ja FOR väga nuditud variant.
Seda ajastul, kui iga programmeerimist õppinu teab Dijkstra kuulsat artiklit
"Goto considered harmful".<17>
Arendajad lahendasid need mured ära - uued programmijupid koos moodsama for tsükliga pandi nn. Extension-ite alla, aga igaks juhuks otsustati, et võiks teha võimalikuks ka neid mitte kasutada.
Globaalsete muutujatele lisaks mõeldi välja võimalus tekitada lokaalseid muutujaid SETLOCAL ja ENDLOCAL vahel.

Ja kõige lõpuks otsustati spetsiaalse võtme abiga - enableDelayed Expansion panna muutujad muutma oma väärtusi ka käsuplokkide sees. Aga seda kõike ikka ka nii, et vanaviisi, kus muutujad ei muutu mitte, saaks ka edasi jätkata. Kellele sellise jama jätkumist vaja oli, jääb saladuseks.
Ja kõige tipuks pandi need 3 täiesti üksteisega mitte seotud asja ÜHE käsu alla!
Alustame tõeliselt heast uudisest.
.bat failides, kui alustada tööd peale @echo off väljastamist lausega
SETLOCAL, jäävad kõik muudatused .bat faili piiridesse. Globaalsete keskkonnamuutujate väärtused cmd.exe akna jaoks salvestatakse sellistena, nagu nad olid ja pärast bat programmi töö lõppemist taastatakse endine olukord. Kõik muutujad bat faili sees ja ka globaalsetesse muutujatesse tehtud muudatused haihtuvad. Globaalsete muutujate väärtused paistavad bat failile aga läbi. Sama asi jätkub ka siis, kui .bat fail kutsub välja teise .bat faili või enda sees peituva funktsiooni - SETLOCAL ja ENDLOCAL bat faili või funktsiooni sees tagab, et muudatused jäävad selle .bat faili või funktsiooni sisse.
See on väga hea asi programmeerimist natukenegi tundvale, sest kaitseb ühte programmi teise eest.
Olgu minu programmis muutuja count, mis loeb ära, mitu korda ma pean mingit tsüklit tegema ja vahepeal välja kutsuma programmi B. Programmi B kirjutaja võis kasutada sama muutujat mingi teise tsükli tegemiseks ja sealt tagasi tulles on minu count ära muudetud.
Nüüd aga kirjutab ta oma programmi algusesse sisse SETLOCAL. Siis kui B vajab nüüd ka kas minu programmi, või üldise cmd akna muutujate muutmist, kirjutab ta ENDLOCAL, muudab need globaalid, mis vaja, ära, ja alustab jälle SETLOCAL sessiooni. Enne oma programmi lõppemist lõpetab ta programmi töö ENDLOCAL-ga.
Muutuja count muutmine tema programmis minu programmi ei mõjuta, ma võin välja kutsuda B-d palju tahan.
Kõikide ühiste muutujate muutmine peab olema muidugi omavahel viimseni kooskõlastatud, need on kohad, mis on kõrgendatud veariskiga...
Väljakutsuva programmi muutujate muutmise eest ei ole kaitset väljakutsujal, sellega peab tegelema funktsiooni või alamprogrammi kirjutaja. Kui ta seda ei tee, on asjad pekis, s.t. B saab minu muutujad, kohtades, kus ta on unustanud  SETLOCAL / ENDLOCAL-i, vussi keerata.
Kui B muudab midagi globaalse käsuakna keskkonnamuutujates, peab see kusagil selgelt kirjas olema ja minu programmiga on täpselt sama lugu...
Niisiis alustage oma bat faili käsuga pärast @echo off -i
SETLOCAL käsuga.
Alamprogrammid / failid / programmijupid, mida suure pingutusega võib kutsuda ka funktsioonideks, s.t. mida kutsute välja CALL -iga ka teie faili sees, ka seal peab kõik algama sama lausega SETLOCAL.
Alamprogrammist väljudes peab olema lause ENDLOCAL.
bat failist väljudes cmd.exe käsurea peale tagasi tehakse see automaatselt.
Aga nüüd tulevad SETLOCAL käsule manukad, keda üldse vaja ei ole:

a) Laienduste lubamine. Teie õnneks enamik moodsaid (isegi XP on moodne bat failide mõttes) masinaid paneb cmd.exe käima nii, et laiendused on nii ehk nii sees. Aga on üks väga paha asi - need laiendused saab mingil viisil registris välja lülitada. Et teie poolt käivitataval cmd.exe -l need ikkagi olemas oleksid, on kaval cmd.exe käima panna /E:ON võtmega.
Aga programmide kirjutaja peab arvestama igasuguse loomaaiaga issanda arvutipargis. Mõnel arvutil võib see asi olla registris kuidagi disabled ja seetõttu peab tema arvutis programm kindlasti alustama oma tööd nii:
SETLOCAL enableExtensions
/ suur väiketähed W-s ei loe siin, aga KOHATI LOEVAD !! , aga mingit tüüpi suur-väiketähtede kokkulepped oleksid hea asi. Saab paremini programme lugeda.
Aga nüüd ei pruugi kohtades, kus programmeerija tahab tõesti mõnda cmd.exe akna globaalmuutujat muuta, enam neid laiendeid saada kasutada. Nüüd peab .bat keele õppija kohe kindlasti teadma, millised on laiendatud võimalused ja millised võimalused (seejuures väga piiratud) on need vanad võimalused, et mitte eksida nendes programmi sektsioonides, kus ta peab tegelema globaalsete muutujatega, sest seal kehtib kitsendatud süntaks.
Seda lihtsalt seetõttu, et kaks käsku on pandud ÜHTE PATTA.
Sellist omadust kutsutakse mitteortogonaalsuseks.

b) Muutujate muutmise lubamine.
Aga kus kaks vana sõpra juba ees, las tulla kolmaski - omadus enableDelayedExpansion võimaldab muutujaid kasutada ka nii, et ploki sees muutuja väärtused tõesti ka muutuksid....
Aga sektsioonides, kus tuleks muuta globaalseid muutujaid, ei saa te seda omadust tingimatult jõustada .
Õnneks neid ei tohiks palju ette tulla, aga küsimus on ka natukene põhimõttes - miks peab 3 täiesti erinevat asja olema kokku keeratud ühte patta?
Niisiis peate alustama oma programmi tööd nii:
@ECHO OFF
SETLOCAL enableExtensions enableDelayedExpansion

Nende tegevuste edukuse kohta (peaks olema iga DOS käsuga enam-vähe nii) on leiutatud suhteliselt ebaõnnestunud nimega muutuja ERRORLEVEL. See on siis, kui käsk õnnestub, 0 ja kui ebaõnnestub, 0-st erinev, sisaldades veakoodi. 
Selline natukene äraspidine loogika kehtib ka Unix-is ja on õigupoolest pärit C keelest. Ei saa seda just väga õnnestunud konstruktsiooniks lugeda, C keele omapära tõttu on see C-s põhjendatud, aga ta on kandunud ka mujale. Ometi saab sellega vähemalt teada, kas enableExtensions õnnestus või mitte.
Enamasti ei viitsi .bat programmeerijad sellega vaeva näha, ja õigusega - normaalsed arvutitel peab see asi toimima ja kui ei toimi, ei ole seal mingite asjade toimetamine eriti mõtekas. Las uurib oma IE 6.0-ga mingit internetisaiti ja piisab sellestki ...

Aga isegi siis, kui lause
SETLOCAL enableExtensions enableDelayedExpansion
on jõus, ei muutu protsendimärkidega tsüklites, ülaltoodud näited, mitte kui midagi.
Et muutujat muuta, peate muutuja sisule viitamisel kasutama kahte hüüumärki.
Ja programmi sektsioonides, kus tahetakse muuta globaalseid muutujaid, ei saa te sellist asja üldse kasutada!!
Ühesõnaga - pärast neid settinguid käitub plokk
SET hei=hai & echo !hei!
täiesti mõistlikult.
Saate vastuseks hai.
Sellega lugu siiski ei piirdu. batil on varuks iga nurga peal krutskeid.
Proovige sellist plokki -
SET hei=hai & echo !hei!!hei!
Saate
hai hai
Nüüd on vist selge, miks on omistamisel vaja kirjutada
SET "hei=hai" & echo !hei!!hei!
tuleb
haihai
nagu oligi mõeldud...
Programmis on nüüd palju kohti, kus ühel ja samal muutujal on kaks erinevat väärtust - ühe saab kätte %hei% -d kasutades, teise !hei! -d kasutades.
Lisaks kõigele muule lisab !hei! notatsioon erisümbolite ridadesse hüüumärgid ja paneb isegi siis, kui muutuja hei sisaldab TÄPSELT samasid märke nii ühte kui teisipidi vaadeldes, echo %hei% ja echo !hei! erinevalt käituma.

Võimalik ainult Venemaal ja MS-s!

III Põnev probleem batmannidele - tühja rea väljastamine.

Ülesanne: Väljastage bat/cmd failist hello ja world vahele tühi rida.
Väga raske ülesanne: teha seda ÜHE echo käsuga. (vt. IV sektsiooni selle lahenduse osas).
Algajalik lihne echo valmistab kohe üllatuse.
echo is off
või
echo is on
tuleb vastuseks. Sõltub sellest, kas olete oma kaja välja lükanud või mitte.
Selgub aga, et tühja rida võib väljastada päris mitmel viisil
näiteks
echo.
echo:
echo,
echo(
ja selgub näiteks, et pärast
echo tere >echo -t (tekitate faili echo ja kirjutate sinna tere)
echo. enam ei toimi! Teile vastatakse endise viisaka reavahetuse asemel nipsakalt:

'echo.' is not recognized as an internal or external command,
operable program or batch file.

Väidetavalt echo( ainukesena ei pidavat tekitama jamasid ... aga te kohtate päris sagedasti echo. konstruktsiooni, või echo/ konstruktsiooni. Ja ei ole viitsinud uurida, mis siis echo/ -ga näiteks lahti on.
Nii et lahenduseks oleks
echo hello & echo( & echo world
vt <13>

IV Täiesti erilised erimärgid ja tühja rea väljastamise erimuutuja.

Bat olukord pärast enableDelayedExpansion-i elluviimist sümbolite osas on siiski paranenud. Enne seda oli asi päris hull. Praegu, ette teades, et erisümbolitega võib tulla jamasid, on siiski võimalik nendega toime saada.
See, et echo %hei% ja echo !hei! käituvad erinevalt, on pigem hea kui halb. Ja enamasti see, mis
echo %hei% teeb, ei ole hea ja echo !hei! väljastab täpselt muutuja hei sisu, kõigi seal sisalduvate sümbolitega. Eriprobleemiks on aga need sümbolid sinna muutujasse saada.
Näide probleemidest %xxx% notatsiooniga:
Tippige
set "hei=hello & world" .
(siin set "xxx=zzz zzz" võimaldab muutujale xxx sisse smugeldada erimärke)
Nüüd echo %hei% annab juba tuntud vea, sest & järel tahab täitma hakata järgmist käsku
echo !hei! saab hei sisu avaldamisega ilusti hakkama, sest ei interpreteeri !hei! stringi mitte mingil moel.
Nii et kasutage !xx! notatsiooni
Erisümbolite sissesmugeldamine muutujasse aga ei ole alati lihtne. Toon näiteks reavahetuse NL tekitamise:
Muudes keeltes on vaja õigele kohale kirjutada \n ja see toimib kui reavahetus ...
Bat keeles on asi keeruline.
Aga saab.
Selleks peate kirjutama


set LF=^


REM need reavahetused on vajalikud
Nüüd
echo hei!LF!hei
annab
hei
hei

^  sümbolil on kaks tähendust: kõigepealt on ta paosümbol -
^ abiga võite pääseda sümbolite > < & ... eritähendustest
Näiteks echo hallo ^& world
annab
hallo & world
Aga samal sümbolil on kahjuks/õnneks ka teine funktsioon – pikkade ridade pikendamise võimalus.
järgnevat reavahetust lihtsalt ignoreeritakse.
Tippige ntx faili reapikendus.cmd sisse
@ECHO OFF
::SETLOCAL EnableExtensions EnableDelayedExpansion
set hei=hello^
& world
echo Protsendimargiga hei: %hei%
echo Hyyumargiga hei: !hei!
^ siin teeb ühekorraga kahte tööd – märgib ära, et kui tema järel tuleb reavahetuse sümbol, siis me selle jätame tähelepanuta, ning seejärel jätame erikohtlemiseta iga järgneva sümboli (praegu siis &, aga
reavahetuse muutuja loomise näites reavahetuse sümboli enese.

Nii näiteks
echo hei^
>NUL
väljastab hei>NUL
Üritasin illustreerida, et > on väljundi ümbersuunamise sümbol ja ülal üritati väljundit 2 rea peal suunata ümber ei kuhugi (tobedatel illustratiivsetel eesmärkidel, juhendite kirjutajate armastatuim tegevus – välja mõelda tobedusi: tahan öelda echo hei ja seda nii, et keegi ei näeks)
< suunab samal eesmärgil ümber sisendi.
Ja ^ päästab selle märgi eritähenduse eest, käsitledes <, > ... j.t. erimärke tavasümbolitena.

See ei toimi alati – kui kirjutada, nagu ma olen õpetanud set käsku tegema sama asja NII:
set "hei=hello^
>NUL"
saate nende ridade täitmisel veateate koos exit /B 1 /ga (väljumise käsk .bat failis vea korral, väljund 0 tähendab et kõik korras)
The syntax of the command is incorrect.
Reavahetust ei ignoreerita, set käsk täidetakse, järgmine rida on uus käsk, >NUL annab mõttetuse.

Teada kiiks ^ puhul:
% märgi eritähendust ^ millegipärast ei mõjuta. selle asemel tuleks % märk kirjutada TOPELT.
^ mõjutas % märki küll cmd.exe käsureal, aga bat faili sees kirjutati ^%hei^% asemele tühi väärtus, ja lubatud sümbolite joru %hei% ekraanile ei tulnud. Otsitakse taga hoopis imelikku muutujat hei^, mis on tühi.
Sarnase .bat tujutsemise tõttu tuleb iga väidet/konstruktsiooni kontrollida - võib juhtuda, et olete esimene, kes avastab, et see näiteks teie arvutil ei toimi ja tuleb leida teine tee...
Kuidas kasutaja erimärke sisestada võiks:

Kasutajatele andmete sisestamiseks on olemas käsk
set /p hallo=sisestage siia midagi:
siin hallo on muutuja nimi, = märgi järel olev tekst on kasutajale ettetulev prompt.
Võib-olla polegi erisümbolitega asi väga hull, kui võrrelda lausa skandaalse olukorraga, mida näete sektsioonis X - kõige lihtsamad sõneoperatsioonid tekitavad programmeerijale märkimisväärset peavalu - näiteks suurtähtedeks teisendamine, stringi pikkuse leidmine (see fakt on absoluutselt skandaalne), tavaline joondamine etc...
Aga ka täiesti tavaline võrdlemine toob kaasa üllatusi. Sellega sektsioonis V tegelemegi.

V Täiesti erilised kolmetähelised nimetused tingimuste kontrollil - Suurima üllatuse printsiip!

Peaaegu kõik programmeerimiskeeled tavaliselt võrdlevad oma asju ühtemoodi - nagu matemaatikas ammu välja kujunenud on, <, > ja = märke kasutades.
= märk on programeerimises matemaatikalt ära võetud - see on jäetud omistamisele, seetõttu seda asendab ==
kaks võrdusmärki koos. Nii on tavaline käibiv komplekt võrdlusteks
märgid ==, >=, >, <, <= ja != (ei ole võrdne).
Lisaks nendele kasutas näiteks Larry Wall perlis tähiseid lt (less than), gt (greater than), eq (equal), ne (not equal) sõnede võrdlemiseks ja tal oli selleks ka põhjus. Nimelt skriptivates keeltes ei ole tüüpe ja programeerija hooleks on teada, mida ta üldse võrdleb - sõnesid või arve. Vahel mõnes keeles (näiteks JavaScript) tikub programmeerimiskeel liiga tark olema ja enne võrdlust ühte tüüpi andmeid teiseks teisendama, tekitades raskesti avastatavaid vigu...
Nii et lt, gt, eq komplekti esinemine teist tüüpi võrdluseks on õigustatud. Perlis numbreid võrreldakse nagu matemaatikas, stringe aga gt, lt, eq ... abiga.
Meie BAT ei tee ei ühte ega teist. Ta võrdleb oma tarkusega (ise teab, kas ta arvab tegemist olevat arvude või sõnede võrdlusega) kahte operandi ja kasutab nendeks absoluutselt üllatavalt, KOLMANDAT, unikaalset (vt. erinevate keelte võrdlusoperaatoreid <18> ) tähistusviisi, nimelt operaatoreid lühenditega

EQU, NEQ, GTR, LSS, GEQ, LEQ.
Tähendused:
EQU - võrdne
NEQ - ei ole võrdne
GTR - suurem kui
LSS - väiksem kui
GEQ - suurem või võrdne
LEQ - väiksem või võrdne

Ma nimetaksin seda
Suurima Üllatuse Printsiibiks
analoogina Rubys käibivale "Vähima Üllatuse Printsiibile" <19>
Kuid tööd teha saab. Kuigi ruttan ette - tsüklist kohe allpool, saab IF võrdlust kasutades sellise progejupi kirja panna, mis täisruute väljastab
btw võta töötav jupp...
set count=0
:ruudud
if !count! LSS 10 (
set /A countruudus=!count!*!count!
echo "Arvu !count! ruut on !countruudus! !"
set /A count+=1
goto :ruudud
) else (
echo "Ruudud arvutatud!"
)
pause >NUL

Pause peatab programmi töö, oodates klahvivajutust. Muidu teatest press any key lahti ei saa,
kui suunata see NUL-i, enne seda peate echo-tama teate, mida tegelikult tahate kasutajale öelda...

VI Värdnimedega muutujate lubamisest

Kunagi oli NL-s väga populaarne laul teemal et ma teist niisugust maad ei teagi, kus nii vabalt hingab inimene....
Ma ei tea ühtegi teist skriptimiskeelt (kes teab, andke märku!), kus nii vabalt saab meisterdada muutujaid. Seda peaaegu ükskõik milliste nimedega. Loome näiteks muutujad nimedega ‘.’, ‘:’,
. ‘ – lugesite õieti – punkt ja kaks tühikut. Kõik on võimalik! Ei usu?

c:\bat>set ".=PUNKT"

c:\bat>set ",=KOMA"

c:\bat>set ":=KOOLON "

c:\bat>set "==VORDSUS"
The syntax of the command is incorrect.

c:\bat>set ". =PUNKT JA KAKS TYHIKUT"


Ja vaatame oma loomaaia käsuga SET üle:

,=KOMA
.=PUNKT
. =PUNKT JA KAKS TYHIKUT
:=KOOLON

Ainult võrdsusmärki ei õnnestu muutujaks defineerida, aga ma ei ole kindel, kas seegi ei võiks kuidagi läbi minna...

Ma ei tea ka mitte ühtegi mõistlikku põhjust sellise anarhia lubamiseks peale selle, et oleks huvitavam ja lõbusam, nagu NL-s oligi.
Mõnikord lihtsalt mõni asi ei klapi. Näiteks käsurea peal saab meisterdada muutujaid %1, ...%9.
.bat skripti sees hästi ei saa (mul ei tulnud välja, aga mine tea, äkki kellelgi tuleb)...
Kui pea tööle panna, saab nii genereerida väga absurdseid vigu ja programmi väga absurdselt käima tõmmata. Häkkerite paradiis ja normaalsete programmeerijate õudusunenägu.
Normaalsed keeled, nagu Perl, piiritlevad selle mängumuru väga selgelt. Alakriips, ladina tähed, numbrid, aga mitte mingil juhul ei tohi muutuja alata numbriga.
.bat-s pole probleemi.

c:\bat>set 5=25
c:\bat>set 5
5=25
...
ja nii edasi ja nii edasi, nagu Vonnegut armastas kirjutada ...

VII Kuidas kommenteerida .bat faile.

Ma ei tea ühtegi teist programmeerimiskeelt peale .bat keele, kus ametlik kommentaaririda
võib põhjustada ametliku veateate.
Kirjutage selline programm:

@echo off
echo hei
REM See kommentaar on vigane! %~...
echo see oli vigane kommentaar, mistqttu programm siia ei jqudnud !

Saate vastuseks omapärase tiraadi:

hei
The following usage of the path operator in batch-parameter
substitution is invalid: %~...

For valid formats type CALL /? or FOR /?

Kaotage need õnnetud %~ ... tiraad kaob:

hei
see oli vigane kommentaar, mistqttu programm siia ei jqudnud

REM ei ole pigem kommentaar, vaid nagu käsk, mis ei tee midagi. Nii saab mõne teise käsuga koos
moodustada plokke, kus &REM järel tulev jutt paistab nagu realõpukommentaarina.
echo hei &REM siia saab ka kommentaarida...
Kui echo on sisse lülitatud, siis REM tekst paisatakse ekraanile ja kui väljund on ümber suunatud, siis kuhugi faili. Seetõttu võib seda koguni kasutada millegi kirjutamiseks faili, nagu käsku.
Nii tavaliselt ei tehta - kasutada kommentaari käsuna. bashis on olemas nii mittemidagi tegemise käsk ja kommentaarimärk ( : ning # ).
Unix-is on deviis, teha ühte asja ja teha seda hästi. Kuna REM tahab teha mitut asja, s.h. mittemidagitegemise käsku, siis ei tee ta mõlemat hästi.
REM on aeglane, võrreldes REM asendamiseks väljamõeldud häkiga ::
See häkk põhineb sellel, et : -ga algavad märgendid (label) ja kaks koolonit on selline vigane märgend, kuu GOTO kunagi ei saa jõuda ja sinna saab igasuguseid asju kirjutada lõppu.
Üldse saab märgendite taha kirjutada juttu juurde, niisiis ka :: taha.
Et kahe kooloniga kommentaarid jooksevad kiiremini kui REM kommentaarid on järjekordne programmeerimisalane huumor, aga nii on.
Ka :: ei päästa %~ -le järgneva tiraadi eest, võite proovida.
:: -l on veel üks häda – käsuplokkide sees põhjustab veateateid.
Võib-olla on kõige lihtsam lugeja elu veidi keerulisemaks teha ja kommentaarid asetada käsuplokkide ette või taha, seal ei tohiks palju probleeme olla – või siis ikkagi käsuplokkide sees kasutada neid REM-e, kui muidu ei saa.
:: näevad välja ilusamad. Nii arvavad paljud DOS häkkerid.

VIII Vältimatu GOTO, hoolimata Dijkstrast – tsüklid ja funktsioonid

Tänapäeva moodsates keeltes enam GOTO-d ei ole, põhjuseks Dijkstra artikkel <17>.

GOTO on aga alati olemas olnud ja jääb alati assemblerikeelte raudvarasse koos alamprogrammi väljakutsumise käsuga CALL, mille lõpetab return käsk, mis viib alamprogrammist tagasi CALL-le järgneva assemblerirea peale.
Protsessor lihtsalt masinakäskude tasemel muust ei ole võimeline aru saama.
Selles mõttes .bat keel on assemblerilähedane. Lisaks on olemas for tsükkel, aga väga iseäralik – teiste keelte for tsüklite tundmine ei aita, vaid hoopis segab selle omapärase käsu uurimisel.
Funktsioone ei ole, seda asendab CALL :label, nagu assembleriski.
For tsüklite “omapärade” tõttu ei ole alati võimalik kõiki tsükleid for abiga teha ja peabki selgeks õppima klassikaliste assembleristide tavarelvastuse.

Kuidas .bat-s (ja assembleris) tsüklit teha

Tippige (ntx faili sum100.cmd)

@ECHO OFF
SETLOCAL enableExtensions enableDelayedExpansion
set /A i=1
set /A sum=0
:LOOP
SET /A sum+=i
SET /A i+=1
IF !i! LEQ 100 GOTO :LOOP
echo Arvude summa 1..100 on !sum!
pause >NUL

Peate saama:

Arvude summa 1..100 on 5050
Noor Gauss põhjustanud selle summa momentaalse arvutamisega oma matemaatika õpetajale palju meelehärmi, sest see tahtis lapsed pikemalt tööle panna.

SET /A (aritmeetika) valik võimaldab muutujatel esineda ka oma nime all, ilma ! ! või % % ümbriseta, % % ümbris või ! ! ümbris on endiselt võimalik ja selles tsüklis võib mõlemat kasutada, for tsüklis näiteks enam mitte, vaid ainult ! ! ümbrist, sest % ümbrise väärtused ei muutu rohkem kui 1 kord...
Funktsioonilaadse objekti tegemise näiteid .bat –s.

Tippige (ntx faili sumA_B.cmd)

@ECHO OFF
SETLOCAL enableExtensions enableDelayedExpansion
SET A=1000000000
SET B=1000000000
:: programm arvutab A ja B summa 2-l erineval viisil.

CALL :sumABvigane %A% %B%
ECHO sum on nyyd %sum%
set sum=0

CALL :sumABlocal %A% %B%
ECHO sum on nyyd %sum%

:: EXIT kindlasti enne funktsioonide tulekut, muidu saate imelikke veateateid...
EXIT /B

:: Funktsioonide plokk siia programmi peakood ei tohi sattuda.

:sumABvigane A B -- globaalsesse muutujasse sum kirjutatakse A ja B summa.
:: siin on meelega sisse kirjutatud kood, mis muudab globaalseid muutujaid A ja B
SET A=%1
SET B=%2
:: %A% %B% / !A! !B! asemel saab set /A -sse kirjutada A ja B
SET /A sum=A + B
:: GOTO :EOF on synononyym EXIT /B 0 -le. 0 ei ole vajalik, EXIT /B –st ka piisab.
GOTO :EOF

:sumABlocal
SETLOCAL
SET A=%1
SET B=%2
set /A sum=A + B
ENDLOCAL & SET sum=%sum%
EXIT /B

Programmi käivitusel saate 2 korda summa A ja B,
(seda kontrolliks, et olete asjad õieti tippinud /pasteerinud).

Siin saate ka pärast eksperimenteerida aritmeetika limiitidega - pange näiteks A=2000000000 ja B=3000000000, siis hakkavad tulema jaburad tulemused - õige vahemik, kus .bat aritmeetika kehtib, on W XP-st alates
2147483648 ... 2147483647 ja 2000 ja madalamad masinad tunnistavad ainult positiivseid arve, seda vahemikus 0 .. 4,294,967,295.
Koodi uurimine:

A: F-ni väljakutsumine

Rida
CALL :sumABvigane %A% %B%
kutsub välja samas failis oleva funktsiooni sumABvigane
Teisi .bat faile saab ka välja kutsuda, siis :xxx asemel on vaja selle faili nime (samas kataloogis) või pikka teed selle faili juurde ilma koolonita.
Teistes .bat failides olevaid funktsioone ei saa nii välja kutsuda. Muidugi saab selle .bat faili ette ehitada mingi erilise analüsaatori, mis siis CALL järele kirjutatud mingist eriparameetrist (ütleme esimene parameeter) loeb välja, mis FUNKTSIOONI tegelikult taheti saada.
Ma siiralt loodan, et pääsete enne .bat palatist ja ei hakka selliseid imelikke asju välja mõtlema...
CALL xxx järele saab kirjutada igasuguseid parameetreid. %A% ja %B% panevad nendeks parameetriteks
muutujate A ja B väärtused.
!A! ja !B! oleks isegi ohutum, aga praegu ei puhu see pilli.
Kui te oleks kirjutanud
CALL :sumABvigane A B
oleks te funktsioonile üle andnud muutujate A ja B nimed.
Seda tehke ainult siis, kui tahate tagasi saada muutujatesse mingeid väärtusi funktsioonilt. Praegu me nii ei tee, me oleme kokku leppinud, et tagastatakse väärtus muutujasse sum.

Me ei ole aga kokku leppinud, et funktsiooni sumABvigane programmeerija hakkab laamendama globaalses nimeruumis ja kirjutab üle või tekitab seal mingid suvalised muutujad A ja B.
(Võib aga juhtuda, et programmeerija oli hajameelne ja unustas mõned tähtsad read ära. Millised, kohe allpool)
sumABvigane väärtuste üleandmise korral %% märkide abiga kogemata töötab nii, et ei tee kahju isegi ülemprogrammile.
Globaalne muutuja A saab oma väärtuseks üleantud A väärtuse %A%, s.t. jääb samaks ja B saab oma uueks väärtuseks väärtuse %B%. Seejärel arvutatakse summa ja kõik töötab laitmatult.

Kui sumA_B programmeerija oleks kirjutanud programmi väljakutsumisel %A% ja %B% asemele A ja B, oleks funktsioon sumABvigane globaalsete muutujate A ja B väärtuseks saanud tähed A ja B.
SET /A sum=A + B oleks asendanud, nagu ennegi A tema väärtusega A, enne oleks seal olnud 1000000000, ja B tema väärtusega B (enne 1000000000).
Aritmeetika korral on kõik sõned võrdsed 0-ga, tulemuseks oleks tulnud 0.

Vastus oleks tulnud õige, kui summa arvutamise funktsiooni programmeerija ei oleks tegelenud parameetrite ümberomistusega oma muutujatesse ja oleks kohe kirjutanud
set /A sum=%1 + %2
%1 väärtus oleks olnud küll A, aga A väärtuseks oleks leitud globaalsest nimeruumist 1000000000
ja B väärtuseks analoogiliselt 1000000000.
Nii ei ole siiski õige teha. Parameetritega otsemanipuleerimise asemel oleks ohutum nad siiski ümber kirjutada LOKAALSETEKS muutujateks.
(pseudo)lokaalseteks täpsemini, aga tühja sellest.
Õige viis funktsiooni sees õiendmiseks on toodud funktsioonis
sumABlocal

Muutujatega A ja B toimunud ei mõjuta väljakutsuja funktsioonis muutujate A ja B väärtusi, nende väärtused taastatakse ENDLOCAL käsule järgnevas plokis.

Kuidas aga informatsiooni funktsiooni seest tagasi saada?
Selleks tehakse selline trikk - paneme ENDLOCAL-iga samasse plokki veel lauseid. Kuna veel ei ole SET endine komplekt muutujaid taastatud, kehtivad kõik SETLOCAL sees olevate muutujate väärtused. Omistuse sum= puhul aga otsitakse üles juba sum globaalne muutuja (või luuakse) ja
nii omandabki sum=%sum% mõtte – globaalne sum saab lokaalselt ümberarvutatud sum väärtuse.
Mingil imelikul viisil pärast muutujakomplekti taastamist jääb see jõusse.
Seda jama kutsutakse veel tunneldamiseks ja mingit mõistlikku pikka selgitust ma leidnud ei ole.
Ehk leiab selle raamatust <0>
B: Väärtuste tagastamine funktsioonist väljundmuutujate kaudu

%0, %1 … ei ole muutujad erinevalt näiteks Perlist ja teistest keeltest, kus käsuparameetreid käsitletakse täpselt samade grammatikareeglite järgi, kui teisi muutujaid. See fakt teeb ettevaatlikuks, ei tasu neid muutujaid manipuleerida, ilma, et täpselt teaksite, mis toimub.
%0 on väljakutsutava .bat/cmd faili nimi. %1 , %2 … on väljakutsumise rea pealt üleskorjatud parameetrid, nagu cmd oskas neid tõlgendada...
Kui lugeda funktsioonis või .bat/cmd programmis %1 ...%9 väärtusi, saab tõesti need sõned, võimalike modifikatsioonidega, mida on funktsiooni väljakutsumisele reale antud.
Aga mis saab siis, kui teha omistusi stiilis
%x=mingi väärtus

Toon ühe mõtlemapaneva testprogrammi:

@ECHO OFF
SETLOCAL enableExtensions enableDelayedExpansion
:: programm uurib %1 ja %2 omistuste tulemeid alamprogrammi sees
set "hello=hallo"
set "world=maailm"
Call :helloworld hello world

echo hellost sai %hello%
echo worldist sai %world%

echo Nyyd arvuparameetrid
PAUSE

Call :helloworld 4 3
echo 4 on !4! ja 3 on !3!
PAUSE


Call :helloworld %3 %4 yks kaks

echo yks on !yks! ja kaks on !kaks!
pause >NUL
EXIT /B

:: functions block.
:helloworld
::SETLOCAL
echo %1 %2
set "%1=ai"
set "%2=oi"
echo %1 %2
::ENDLOCAL
GOTO :EOF

Väljund:

hello world
hello world
hellost sai ai
worldist sai oi
Nyyd arvuparameetrid
Press any key to continue . . .
4 3
4 3
4 on ai ja 3 on oi
Press any key to continue . . .
yks kaks
yks kaks
yks on ai ja kaks on oi

Selgitusena.
Programmeerija Juku pidi väljastama paar sõnumit ekraanile. Esimene sõnum tuli parameetritest ja teine sõnumi jaoks ta ei tahtnud muutujaid raisata, ka SETLOCAL ja ENDLOCAL jäävad nii ära –
muutujateks on %1 ja %2.
Kahjuks programm väljastas kaks korda täpselt sama sõnumi, omistused
set %1=ai ja set %2=oi ei mõjunud kuidagi.
Peaprogrammis aga avastati, et muutuja hello väärtusest hallo (tõlge eesti keelde) oli saanud ai ja world-st oi (pidi olema maailm). Teises väljakutses olid tekkinud imelikud numbrilised muutujad
number 4 nimeline muutuja oli ai ja 3 oli oi.
Kolmanda väljakutse tegi üks häkker, kes üritas sellega selgeks teha, milles viga on.
Jukul tuli programm ümber teha, kirjutada oma tekst SETLOCAL ja ENDLOCAL vahele ning kasutada oma muutujaid ning lisaks selgeks teha, milleks on omistused %1= ja %2=
Vasakul pool omistus/võrdusmärki on %1 ja %2 viited kas juba olemasolevatele muutujatele, või need luuakse selle omistuse käigus. SETLOCAL ENDLOCAL vahel olev tegevus väljakutsuvasse programmi ei jõua, küll võivad tekkida jaburad süntaksivead...
Kuna muutujate nimede suhtes on .bat-s absoluutne vabadus, siis juhtub seda küllalt harva.
Omistusi %y=xxx tasub teha enamasti vaid väärtuste tagastamisel, ENDLOCAL-st väljas, aga selle käsuga samas plokis olles.
Enne tasuks ehk kontrollida, kas väljakutsuja muutujanimi vastab skriptivates keeltes väljakujunenud standarditele ning muidugi ei ole tühi. Väiksemates programmijuppides ehk ei ole vaja nii paranoiline olla...
Nii võiks välja näha sumA_Bref.cmd, kus väljakutsuja programm määrab, mis muutujasse tulemus kirjutada:

@ECHO OFF
SETLOCAL enableExtensions enableDelayedExpansion
SET A=1000000000
SET B=1000000000
ECHO Programm %0 arvutab muutuja nime edasiandmise viisil funktsioonile
ECHO arvude %A% ja %B% summat!
set sum=0

CALL :sumAB %A% %B% tagasi
ECHO Summaks sai %tagasi%

pause
exit /B 0

:: Funktsioonide plokk

:sumAB A B tagasi -- Arvutab muutujate A B summa ja tagastab summa väärtuse
:: muutujaviitesse %3.

SETLOCAL
SET /A sum=%1 + %2
ENDLOCAL & SET %3=%sum%
GOTO :EOF

C: Sisendinfo kättesaamine muutujaviidete kasutamise korral.
On võimalik, et mõnikord ei ole informatsiooni üleandmiseks alamprogrammile muud head meetodit, kui muutujanime üleandmine. Sel juhul on vaja lahendada omaette probleem - saada kätte muutujanime taga peituv informatsioon.
Näide:

@ECHO OFF
SETLOCAL enableExtensions enableDelayedExpansion
:: programm uurib %1 nime ja va"a"rtuse ka""ttesaamist funktsioonis valByRef
:: pole mahti tegelda ta"pita"htedega, sellest vo~imalik, et hiljem. utf-8 ei toimi cmd.exe-s ha"sti.
set tagasi=65537 &REM Fermat viies ja viimane Fermat algarv, nagu arvatakse.

Call :valByRef tagasi
PAUSE >NUL

EXIT /B

:: functions block.
:valByRef
SETLOCAL
set muutujanimi=%1
set muutujaval=!%muutujanimi%!
echo muutujanimi on %muutujanimi% ja muutuja va"a"rtus on %muutujaval%
ENDLOCAL
GOTO :EOF

Ühesõnaga, muutuja taga peituva muutujanime tegeliku väärtuse saab kätte !% kombinatsiooniga.

Tulemus:

muutujanimi on tagasi ja muutuja va"a"rtus on 65537

D: EXIT kasutamine funktsiooni tulemuse tagastusel

EXIT /B –le saab lisada ka numbrilise tagastusväärtuse, mida väljakutsuja saab lugeda
ERRORLEVEL nimelisest muutujast.
Seda saaab kasutada vaid numbriliste arvutuste puhul.
Selleks tuleks ülaltoodud programmis asendada read
CALL :sumAB %A% %B% tagasi
ECHO Summaks sai %tagasi%

ridadega
CALL :sumAB %A% %B%
ECHO Summaks sai %ERRORLEVEL%

ning funktsioonis :sumAB ENDLOCAL plokk käsureaga
ENDLOCAL & EXIT /B %sum%






IX FOR veidruste anatoomia

Kes vähegi tahab .bat keeles tegija olla, peab ära õppima .bat ainukese korralik olla tahtva tsüklikonstruktsiooni nimega FOR. Kui muidugi assemblerilaadne GOTO välja arvata.
Kahjuks GOTO konstruktsiooniga tsükkel on korralik ja FOR konstruktsioonid ei ole.
Muutujatest, mis % % vahele panduna ei muutu (ja lõpuks ! ! vahel muutuvad, sai juba räägitud.
Nüüd on aeg uuteks üllatusteks.
Alustame grammatikast:

For cmd.exe käsurea pealt sisestatuna:

FOR < /D /R /F /L>  %tsüklimuutuja
in (failid/fail/käsu tulem/muutuja)  do  (
käsud
)

Märkus:
do (

)
asemel võib olla ka do <üks käsk> või ka mitu käsku plokis &, &&, || abil ühendatuna (mis teeb ka ploki). && ja || kohta vt. allpool.

For .bat faili sees - ainuke erinevus - %tsüklimuutuja asemel on nüüd %% tsüklimuutuja -

FOR < /D /R /F /L>   %%tsüklimuutuja
in (set/file/commands) do  (
käsud
)
Tsüklimuutuja tahab omaette käsitlust. See on jälle uus objekt ja erinev nii muutujast kui ka parameetritest. Parameetritega on tsüklimuutuja natuke nagu sugulane, kuna tsüklimuutujaga saab teha samasuguseid teisendusi, mis parameetritega, v.a. viited.
Tsüklimuutuja saab olla vaid üks täht / sümbol järgmistest ASCII sümbolite reast:

ASCII 63 - 93 - 31 võimalust ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ]
ASCII 95-123 29 võimalust :_ ` a b c d e f g h i j k l m n o p q r s t u v w x y z {

Milleks tsüklimuutuja %%{ hea on??
Ilmselt tunneb ta ennast hästi teiste samasuguste värdmuutujate keskel...
Miks tavaline muutuja ei kõlba?
Miks käsurea peal kasutatakse ühte tähistust ja .bat failis teist?
Miks nii palju erilisi tsüklimuutujaid üldse vaja on?

Rohkem kui ühte tsüklimuutujat läheb vaja vaid FOR /F võtme puhul, mis võtab oma sisendist (mingi fail, aga ka mingi käsk, võib - olla koguni muutuja) ühe rea korraga ja jagab selle juppideks.
Mulle ei meeldi sõna jupp, aga midagi paremat ei oska ka pakkuda termini token kohta.
FOR /F ei saa jagu rohkemast kui 31 jupist, hoolimata sellest, et tegelikult on ju 60 võimalust ette nähtud. Siis tuleb teha mitut FOR-i ja neid kuidagi kombineerida...
Tsüklimuutujad on muuseas globaalsed - tsüklimuutuja järgmise sisemise FOR sees näeb välimise FOR tsüklimuutujat ...
Kui aga meil on mingi .CSV fail, kus näiteks 100 veergu ja tahame seda faili teisendada?
Äkki oleks tulnud FOR alla toppimise asemel leiutada mingi vahend, mis selliste failide analüüsiga paremini hakkama saab?
Kuid kõigest järjekorras ja alustame lihtsaimast FOR tsüklist, ainukesest arhaismist, mis jookseb ka siis, kui laiendid (extensions) on lülitatud välja.

Selle FOR käsu grammatika on järgmine:

FOR %% IN (loend1 loend2 loend3 ...) DO

Näiteks selline käsk (käsurealt ühe % -ga!)
.bat-s
FOR %%a IN (1 2 3 4) DO echo %%a
käsureal:
FOR %a IN (1 2 3 4) @echo %a
Käsureal on vajalik ka @ märk echo ees, mis echo maha võtab selle käsu jaoks, et üleliigset spämmi vältida väljundis.
Hiilgav - tõesti töötav käsk .bat keeles. Jälle üllatus (Suurima Üllatuse Printsiip).
Tegelikult on IN käsk mõeldud toimima failinimede loendina või mallide loendina.
Näiteks:

FOR %a IN (pakk*.* *.cmd) DO @echo %a

Sain oma arvutis bat kataloogis uurimustöö teemale vastavalt joru pakkfailidx.x failinimetusi (selle dokumendi erinevad versioonid erinevates editorides ja kõik harjutuseks tehtud *.cmd failide loetelu.

FOR /L tsükkel, kus ei saadagi tsüklist välja.

Tippige sisse selline fail (minul forever1.cmd), kus kõik toimib normaalselt.

@ECHO %echooff%
SETLOCAL enableExtensions enableDelayedExpansion

FOR %%a IN (1 2 3 4 5) do (
echo %%a
IF %%a EQU 3 GOTO :break
)
:break
ECHO Saime minema!
PAUSE >NUL
EXIT /B 0

Selle tähelepanuväärse vaimusünnitise eesmärgiks on demonstreerida toimivat koodi.
.bat tsüklikonstruktsioonides puuduvad break ja next laused, mis tsüklist välja viivad või järgmisse
fataalsesse ahelasse tagasi paiskavad.
Aga .bat ei ole kõrgkeel - siin on kahjulikuks kuulutatud, aga siiski toimekas GOTO, mis ka tsüklisse sattunu ravimiseks võiks kõlvata.
Iga esmakursuslane mõtleb ka välja, et siin käiakse läbi arvud 1..3 ja siis tuleb tsüklikatkestus ja rohkem tsükliga jamamist ei ole. Ja iga normaalne lugeja mõtleb nüüd seda lugedes, et milleks seda kräppi siin mulle pähe määritakse. On kood, toimib ja milles probleem??


For /L tsükliga nii ei ole.
Selle tsükli üldine kuju on järgmine:
FOR /L %%tähtmuutuja IN (algus,samm,lqpp) DO  
tähtmuutuja käib läbi väärtused algus, algus+samm, .... kuni algus+ N*samm saab suuremaks kui lqpp, väärtus lqpp käiakse ka läbi, kui alguse ja sammuga hästi välja tuleb...

Tippige nüüd sisse järgmine kood (minul forever2.cmd)

@SET echooff=%~2
@SETLOCAL enableExtensions enableDelayedExpansion

@(SET "echooff" 1>NUL 2>NUL)||(SET echooff=OFF)
@ECHO %echooff%

SET lqpp=%~1
(SET "lqpp" 1>NUL 2>NUL)||(SET "lqpp=1000000")

FOR /L %%a IN (1,1,%lqpp%) do (
echo %%a
IF %%a EQU 3 GOTO :break
)
:break
ECHO Saime minema!
PAUSE >NUL
EXIT /B 0

Kuni for-ni tegeldakse programmis sellega, et kuidas forever2 parameetreid sättida. Praeguseks hetkeks piisab teadmisest, et ECHO on lülitatud OFF ja lqpp on 1000000 (miljon), kui forever2 käivitada ilma igasuguste parameetriteta.

Käivitage forever2.
Märkate, et millegipärast on tekkinud umbes 10 sekundiline paus. ( või pikem / lühem, sõltub pillist). Aga koodis on selge sõnaga öeldud, nagu eelmiseski tsüklis, et hoolimata tsüklimuutujast lqpp tehakse tsüklit 3 korda ja siis minnakse :break märgendi peale.
Kuidas see on võimalik, iga tsükli peale tänapäevases arvutis 3 sekundit kulutada?
See ei ole uni, batmanid. See on .bat keel!
Et seda selgitada, lisan ka programmi kirjelduse.
%1 parameetri vaikeväärtus on 1000000. %2 parameetri vaikeväärtus on OFF (echo seis).
Lülitage nüüd echo on ja pange %1 5-ks.
forever2 5 ON
annab järgmise väljundi. Selle üle tasub mõtiskleda.

C:\bat>forever2 5 ON

C:\bat>SET lqpp=5

C:\bat>(SET "lqpp" 1>NUL 2>NUL ) || (SET "lqpp=1000000" )

C:\bat>FOR /L %a IN (1 1 5) do (
echo %a
IF %a EQU 3 GOTO :break
)

C:\bat>(
echo 1
IF 1 EQU 3 GOTO :break
)
1

C:\bat>(
echo 2
IF 2 EQU 3 GOTO :break
)
2

C:\bat>(
echo 3
IF 3 EQU 3 GOTO :break
)
3

C:\bat>(
echo 4
IF 4 EQU 3 GOTO :break
)

C:\bat>(
echo 5
IF 5 EQU 3 GOTO :break
)

C:\bat>ECHO Saime minema!
Saime minema

C:\bat>PAUSE 1>NUL

Niisiis ei tee programm kuulmagi nõudmisest minna märgendi :break peale, kui %%a saab võrdseks 3-ga, vaid ta lihtsalt ei täida küll käske, aga käib tsükli päises ikkagi oma numbrit suurendamas ja kui echo on lülitatud ON, siis väljastab täidetava käsu, kuigi mingi havi käsul seda jälle ei täideta, vaid minnaks tsükli algusesse tagasi.
Kasutajale emuleeritakse GOTO :break käsku, aga ei täideta.

Järeldus:
FOR tsüklite sees ärge kasutage GOTO käsku. Kasutage mingeid funktsioone ja käsku EXIT /B 0,
aga mitte mingil juhul GOTO-d.

Märkus: FOR /F puhul ma küll sellist käitumist ei märganud, nagu FOR /L tsüklis, kuid
stackoverflow põgus sirvimine näitab, et GOTO tekitab küllaga probleeme ka teiste FOR võtmete puhul.

Enne FOR /F üllatusteni jõudmist aga lühidalt programmi algusest.

@SET echooff=%~2
REM - %~2 loeb sisse %2 väärtuse nii, et kui selle ümber on jutumärgid, võetakse need ära.
REM ka for tsüklimuutujatega saab nii teha, lisaks veel igasuguseid trikke. vt. FOR ? -st.

@SETLOCAL enableExtensions enableDelayedExpansion
REM @ SETLOCAL ees ütleb - seda käsku ei väljastata, isegi kui ECHO on ON.
@(SET "echooff" 1>NUL 2>NUL)||(SET echooff=OFF)
SET "muutujanimi" annab ERRORLEVEL 1, kui muutujaväärtus on tühi. || täidab järgneva käsuploki käsu ainult siis, kui eelmine käsk andis vea, s.t. ERRORLEVEL. See tähendab, et kui parameetrit %2 ei ole antud, siis selle väärtuseks, ECHO väärtus siis, tuleb OFF.
&& muuseas täidab järgneva käsuploki käsu siis, kui eelnev käsk oli edukas.

@ECHO %echooff%
REM siin lülitatakse ECHO sisse või välja...
SET lqpp=%~1
(SET "lqpp" 1>NUL 2>NUL)||(SET "lqpp=1000000")
REM FOR tsükli ülemiseks piiriks on vaikimisi 1000000, et tekitada tuntav viivitus.
REM 1000000000 oleks tõeline FOREVER!

FOR /F vigane ja kasutu tulevärk faili sisu analüüsiks

Esimesel pilgul on FOR /F äärmiselt aukartustäratav konstruktsioon.
Selle käsu grammatika on selline:

FOR /F ["valikud"] %%tsüklimuutuja IN 
(failinimede_loetelu) DO käsk või käsuplokk
      
Käsk võtab failinimede_loetelust järjest faile, avab need ja iga 
iteratsiooni käigus loeb failist järjekordse rea.
Faili lõppedes see suletakse, avatakse järjekordne fail ja nii 
edasi ja edasi...
Rida jagatakse valikute sees antud juhtnööride järgi juppideks 
(tokens), millega DO käsk või käsuplokk saab teha operatsioone, 
mida programmeerijal on pähe tulnud nende juppidega teha.
Esimene hoiatus - FOR /F ei tee absoluutselt midagi tühjade ridadega. 
Neid ignoreeritakse, programmeerijal puudub võimalus 
(esimesel pilgul) teada saada, kas rida on tühi või ei ole. 
Teine hoiatus - VAIKIMISI ignoreerib FOR kõiki ridasid, 
mis algavad ; -ga.
Seda saab muuta parameetri eol sättimisega. Kahjuks seda 
parameetrit ei saa ilma spetsiaalse süntaksilise nipitamiseta 
muuta tühjaks, s.t. mingi "kommentaari" 
sümbol peab olema vaikesüntaksis olemas. 
vt. allpool ja veel
<9a>,<9b>
Valikud:
      delims=xxx – Sümbolite loetelu 
eraldajatest juppide (tokens) vahel, mis 
heidetakse rea analüüsil lihtsalt kõrvale. 
Vaikimisi on nendeks sümboliteks tühikud ja 
tab-d. 
      skip=n      Mitu rida jätta analüüsist 
välja faili alguses. Vaikimisi on skip väärtus 0. 
      eol=      See on täht, millega algavaid 
ridu ignoreeritakse. Kui see täht on õnneks
 mingi delims parameetriga antud täht, siis 
seda ei juhtu. Nii saab alati, kui delims 
sümbolid on olemas, eol panna võrdseks delims 
mingi sümboliga ja kõik read, v.a. muidugi 
tühi rida, lähevad käiku.
Vaikeväärtus on semikoolon ; - üsna vastik 
üllatus neile, kes ei ole viitsinud RTFM 
soovitust järgida.  

      tokens=x,y,...,z,* / või ka lihtsalt *.
                  Ütleb, mis juppidega (tokens
for tsüklis üldse tegeletakse. x,y,z on numbrid, 
juppide lugemine algab 1-st. ,* lõpus tähendab, 
et z järel tulev sõne reas (seda juba koos 
eraldajatega) on terviklik jupp. z tagant 
on eraldajad siiski eemaldatud.
Vaikimisi võetakse ette vaid esimene jupp. 
Rida, mis sisaldab 
tere , ... talv, ... veel midagi 
annab tokens=1,* väärtuse puhul puhul esimeseks 
jupiks tsüklile käitlemiseks tere ja  teiseks 
jupiks tuleb talv, kui 
delims=,."   (punkt ja koma, "  lõpetab 
parameetrite andmise.)                        
Kuidas need jupid muutujate vahel ära jagatakse, sellest allpool.
 Praegu lihtsalt võtame teadmiseks, et tokens, delims parameetrite 
abiga jagatakse rida juppideks ja teatud jupid antakse edasi tokens -s 
määratud järjenumbritega do käsuplokile.
tokens võib olla ka vahemik näiteks 1-6 - esimesed 6 juppi.
                  
usebackq     on ebaõnnestunud süntaksi järelm. 
Kuna failinimed võivad sisaldada tühikuid, 
jutumärkidega eraldatud objekt IN sulgude sees 
aga tähistab sinna kirjutatud sõnet (string)
 vaikimisi, siis oli vaja sisse tuua erand. 
Nüüd kui usebackq on sisse lülitatud, siis 
jutumärkide sees võib olla ka failinimi 
(hurraa, lõpuks saab selliseid faile ka uurida, 
kus failinime sees on tühikud). Nüüd 
muutujate sisu / stringi analüüsi jaoks 
on vaja see ümbritseda ühekordsete jutumärkidega,
 mis varemalt jälle tähistas seda, et 
ühekordsete jutumärkide seest võeti mingi 
WINDOWS käsu väljund.
Ja windows käsu väljund tuleb nüüd ümbritseda 
tagurpidi jutumärkidega (backquotes, Esc all
 enamikel klaveritel).
PUHH. Väga õpetlik asi tõestamaks, milline 
käkk lõpuks tekib halvasti välja mõeldud 
programmikonstruktsioonist, kus ühe käsu / 
konstruktsiooni alla tahetakse toppida tuhat asja.

Juppide jagamine tsüklimuutujate vahel

Ja ikka veel ei ole kannatamatule lugejale selge, mis saab juppidest,
 kuidas nad muutujate vahel ära jagatakse. Selle panevad 
paika tsüklimuutuja ja tokens poolt etteantud järjenumbrid.
Oletame näiteks, et selleks muutujanimeks on %%a ja tokens=1,3,*

Uuritavast reast võetakse nüüd arvesse 3 juppi -  esimene , kolmas 
ja kolmanda jupi  järel tulevad jupid kõik koos (*). 
Esimene arvesseminev jupp läheb muutujasse %%a
teine jupp (3.)  %%b-sse, kolmas (*) %%c – sse tähestiku järjekorras.
Kui juppide arv on üle 31, tuleb hakata trikitama, ühes for 
tsüklis ei saa rohkem juppe, kui 31 käsitleda. 
Aga kui delimiter on näiteks ','  (koma), siis ka mitu koma 
teevad sama välja. 
Kuna csv failides kaks järjestikust koma tähistavad nende vahel
 peituvat tühja välja, siis ei saa .bat  FOR /F käsklus hakkama .csv 
failidega ja sellist võtit ei ole ka, et kasutada eraldajana vaid ühte 
delimiteri märki. 

Järelikult selline jupitamine võib sageli olla vigane ja sellest tuleks loobuda.
Mingisugune väljapääs oleks FOR /F –i kasutada vaid faili ridadeks jagamisel,
kuid ma ei ole veel leidnud meetodit, kuidas arvestada tsükli sisse tühje ridu. 
FOR lihtsalt ignoreerib neid!
Kui tühjade ridade mittearvestamine pole oluline, tuleb 
FOR /F parameetrites kuidagi saada delims ja eol väärtused tühjaks. 
Delims tühja väärtusega ei teki probleeme, eol-ga aga tekib – 
vigase programmi tõttu ei saa kasutada jutumärke ja ilma jutumärkideta 
tuleb eol= omistus igal juhul panna parameetrite sõne lõppu. 
Selleks peab erisümboleid peab veel paosümboliga  ^ kaitsma.
vt. lähemalt <9b> 
Ühesõnaga - 
FOR tsükkel, kus %%a saab enese väärtusteks uuritava faili 
andmed.txt’  read, jättes välja tühjad, 
on niisugune:
FOR /F delims^=^ eol^= %%a in (andmed.txt) do echo %%a

Veendusime, et tekstifailide süsteemse analüüsi vahendiks FOR /F tsükkel ei kõlba.

For /F kasutamine sõne sisu analüüsiks:

Kui IN järel tulevas suluavaldises on üks nimetus ja see on jutumärkides, siis see tähistab vaikimisi sõnet / stringi. Kui kaval võti usebackq on sees, siis on tegemist failinimega ja vaid ühekordsete jutumärkide vahele panemisega saab hakata uurima sõnet / muutuja väärtust.
Jutumärkide vahele kirjutatud %muutujanimi% võimaldab analüüsida muutuja sisu.
Näiteks:
set "hei=ai oi ui"
FOR /F "tokens=1-3" %a IN ("%hei%") do @echo %a/%b/%c
annab käsurealt
ai/oi/ui

FOR /F cmd käsu väljundi analüüsiks.

Kui ’FOR /F’ IN suluavaldise nimetus on ühekordsetes jutumärkides või hoopis tagurpidi jutumärkides (backquotes, Esc all, usebackq lüliti sisselülituse korral), hakkab for tsükkel rida realt uurima selle käsu väljundit.
See on küll kohmakas, aga väga vajalik viis ühe funktsiooni / käsu väljundi uurimiseks ilma vahepealset ajutist faili tekitamata.
Mõnikord on need arvutid, kus .bat programm võiks toimida, ära keelanud failide loomise .bat töökataloogis ja ometi peab .bat programm hakkama saama ilma ajutiste failideta.

Näide käsurealt:
for /F "tokens=1-5" %a in ('dir ') do @echo %a %b %c %d %e

Ühe käsu väljund võib oma väljundi toru/pipe sümboli kaudu suunata teise käsu sisendisse.
Sulgavaldise sees tuleb toru sümboli ette kirjutada pääsusümbol ^.
Näide käsurealt:

for /F "tokens=1-5" %a in ('dir ^| find /v /n "" ') do @echo %

või

for /F "tokens=1-5" %a in ('dir /b ^| find /v /c "" ') do @echo %


Find /v "" tegelikult teeb failist /teise käsu sisust koopia, aga seejärel find mõned võtmed on kasulikud:
/n kirjutab ette rea numbri
/c loeb lihtsalt read ära. Esimeses näites andsime järjenumbrid dir väljundile ja teises näites lugesime nii ära, mitu faili oli kataloogis.

PS! Nii saab lugeda ära dir võtme /s abiga ka kõikide failide koguarvu koos jooksva kataloogi alamkataloogidega ....

FOR /R – rekursiivelt mööda puud alla

FOR R üldkuju:

FOR /R  %%tähtmuutuja  IN (mallid) do

Näide:

for /R  % %G  IN  (*.dll *.exe) do @echo %G

loetleb kõik dll ja exe failid käsurealt täidetuna.
Kõik oleks tore, kui see FOR /R ei tooks kaasa üllatusi
Käsu “korraliku” täitmise eelduseks on, et
IN avaldiste mallid peavad tingimata sisaldama erisümboleid, vähemalt üks * või ?
<11>
See ja paljud muud asjad ei ole dokumenteeritud “ametlikus” Windowsi dokumentatsionis.
Näiteks *.jpg sobib, aga pilt.jpg ei sobi – see on konkreetne failinimi.
Veel hullem –
programm annab sellel juhul hulganisti valepositiivseid vastuseid, s.t. käitub vigaselt.
Illustreerin seda näitega käsurealt ...
Olgu mul näiteks oma
c:\bat kataloogis fail ruudud.cmd
Kui ma olen unustanud, kas mul on kataloogis c:\bat selline fail, saab selle olemasolu kontrollida käsuga
FOR %a IN (ruudud.cmd) DO @echo %a
Vastus on õige ja positiivne, nagu võibki eeldata.

ruudud.cmd

Olgu mul nüüd tehtud alamkataloog oma failidest nimega bak ja kõik c:\bat failid mingi kuupäeva seisuga kopeeritud sinna, k.a. ruudud.cmd
Vaatame, kuhu ma olen oma alampuus koos varufailidega ruudud.cmd faili veel pannud:
Kasutame /R lülitit.
FOR /R %a IN (ruudud.cmd) DO @echo %a

C:\bat\ruudud.cmd
C:\bat\bak\ruudud.cmd

Suurepärane!
Aga äkki olen tegelenud .bat keeles ka arvude kuupide arvutusega?

for /R %a IN (kuubid.cmd) do @echo %a

Vastus:
C:\bat\kuubid.cmd
C:\bat\bak\kuubid.cmd

Selgub siiski pärast mõningast järelemõtlemist ja otsimist, et kuubid.cmd faili pole kuskil pool.
FOR /R võtmega tsüklis on sees väga vastik viga – millegipärast eeldatakse IN sulgavaldises kõkide mallide puhul kas * või ? märkide sisaldumist ja mittesisaldumise korral antakse tagasi väärinfot.
Kui mallid ei sisalda metamärke, siis loetletakse kõik kataloogid, mis etteantud kataloogipuus on ja nendele katalooginimedele lisatakse MILLEGIPÄRAST otsitud failinimed IN sulgavaldise seest.

Avatud lähtekoodiga tarkvarade puhul oleks see skandaalne viga ammu kõrvaldatud.
W ei kavatse seda teha iialgi, hoolimata igapäevastest “kriitilistest” uuendustest, mis väga sageli meie väärtuslikku aega raiskavad.

Märkus:
FOR –l on veel lisaks /R –le /D võti. Räägitakse, et /R ja /D –d saab koos kasutada ja räägitakse, et sel juhul uuritakse kataloogipuu katalooginimesid, mitte failinimesid....

Märkus:
W .bat keelt saab parandada. Seda on tehtud W –ga mitte seotud programmeerijate poolt.

Tulemus on saadaval nimega
TCC/LE Windows CMD Replacement Shell <10>

X BAT ja sõned

Kunagi oli väga populaarne lasteraamat pealkirjaga “Peep ja sõnad”.
Peebul oli raskusi suurte inimeste keelest arusaamisega, näiteks “Vesi ahjus”
BAT-l on oma sünnist saati olnud raskusi sõnedega (välismaa keeles stringid). Sõned koos tekstifailidega on aga endiselt programmeerijate peamine lähtematerjal. On programmeerimiskeeli, kus kõik, mis on, ongi sõne: TCL (Ousterhoot).
Kui sõnedega ei viitsita korralikult vaeva näha ja puuduvad sõnetöötluse käsud, siis on programmeerimiskeel peaaegu kindlasti kipakas.
Ka küürakat üritatakse elu jooksul sirgeks saada, kuigi enamasti tulemusteta ...
BAT on samuti ühte kui teist õppinud. Uurime siis, kuidas seda ühte kui teist sõnedaga teha.

A: Ülesanne – teisendada Hallo World suur- või väiketähtedeks.

Sellele ülesandele on pühendatud <1a>.
BAT ei ole suvatsenud ise teha mitte ühtegi funktsiooni sõnede teisendamiseks suur või väiketähtedeks, kuigi ise kasutab neid tehteid oma sisemises tegevuses sageli.
Selle tõestuseks sobib W käsurea käsk find:

C:\bat>find "" ruudud.cmd

---------- RUUDUD.CMD

Viimane rida peab tähendama, et find ütleb, ma ei leidnud ruudud.cmd-st seda sõnet. Kõrvalnähtusena näeme, et ruudud.cmd on teisenenud SUURTÄHTEDEKS.
PS! Find on kasulik käsk.
find “” ei leia kunagi mingist failist mitte midagi. Võtmed /v tähistavad eitust, seega
find /v “” tagastab iga rea. Find-l on olemas võti /c – loeb lihtsalt ära tulemuse – mitu klappivat rida leidis. Järelikult
find /v/c “” loeb üle faili ridade arvu.
find /v/n “” kleebib failile külge rea numbrid.

See asjaolu võimaldab find –i isegi kasutada suurtähtedeks teisendusel – teeme find käsu mitteolemasoleva failinimega, milleks on teisendatav sõne + lisandid ehk, näiteks tühikud, et see fail kindlasti ei oleks olemas ja õngitseme välja tagastatavast reast
file not found ...
teisendatava sõne.
(idee on reaalselt kasutusel).
Näide võimalikust kasutusest:

C:\bat>find "" kuubid.cmd
File not found - KUUBID.CMD

Nüüd oleks vaja vastusereast kätte saada KUUBID.CMD

Selliseid trikke, kasutades .bat sisemisi funktsioone, mis mingi teksti ise suurtähtedeks teevad, on pakutud mitmeid.
Sellest hoolimata on ainukeseks süsteemseks lahenduseks BAT enda mõne sõnetöötlusfunktsiooni rakendamine.
Neid ei ole palju ja ühe tekstijupi asenduseks teisega sõne seest sobib selline SET
käsk:
SET  muutujanimi=%muutujanimi:mida asendada=millega asendada%

Näiteks:

set hei=hai

set "hei=%hei:a=i%"

echo %hei%

hii

See asenduskäsk on asendatava sõne tõstu (capitalization) suhtes tundetu.

set hei=hAi

set "hei=%hei:a=i%"

echo %hei%

hii

See tähttäheline asendustegevus ongi aluseks upper.cmd programmi funktsioonide toupper ja tolower toimimisele.
Idee on võetud saidilt <1a>
(Eesti tähestiku ja utf-8-ga ma siin traktaadis igaks juhuks ei tegele, seetõttu jäävad täpitähtedega asjad nirudeks, minult abi ei saa)

Programm upper.cmd
Idee võetud saidilt <1a>

@ECHO OFF
:: Programm sõne %1 teisendamiseks suur- ja väiketähtedeks, tulemused väljastatakse ekraanile
SETLOCAL EnableExtensions EnableDelayedExpansion
:: ~ märk eemaldab %1 ümbert jutumärgid, kui %1 on nendega ümbritsetud.
SET "sqneU=%~1%"
SET "sqneL=%~1%"

CALL :UpCase sqneU
CALL :LoCase sqneL

echo Etteantud sõne oli: %1
echo Suurtähtedes on sõne: %sqneU%
echo Väiketähtedes on sõne: %sqneL%
pause
ENDLOCAL
GOTO:EOF

:LoCase
:: Funktsioon sõne väiketähtedeks teisendamiseks.
:: teisendatava muutuja nimi on parameeter.
SET %1=!%1:A=a!
SET %1=!%1:B=b!
SET %1=!%1:C=c!
SET %1=!%1:D=d!
SET %1=!%1:E=e!
SET %1=!%1:F=f!
SET %1=!%1:G=g!
SET %1=!%1:H=h!
SET %1=!%1:I=i!
SET %1=!%1:J=j!
SET %1=!%1:K=k!
SET %1=!%1:L=l!
SET %1=!%1:M=m!
SET %1=!%1:N=n!
SET %1=!%1:O=o!
SET %1=!%1:P=p!
SET %1=!%1:Q=q!
SET %1=!%1:R=r!
SET %1=!%1:S=s!
SET %1=!%1:T=t!
SET %1=!%1:U=u!
SET %1=!%1:V=v!
SET %1=!%1:W=w!
SET %1=!%1:X=x!
SET %1=!%1:Y=y!
SET %1=!%1:Z=z!
GOTO:EOF

:UpCase
:: Funktsioon sõne suurtähtedeks teisendamiseks.
:: teisendatava muutuja nimi on parameeter.
SET %1=!%1:a=A!
SET %1=!%1:b=B!
SET %1=!%1:c=C!
SET %1=!%1:d=D!
SET %1=!%1:e=E!
SET %1=!%1:f=F!
SET %1=!%1:g=G!
SET %1=!%1:h=H!
SET %1=!%1:i=I!
SET %1=!%1:j=J!
SET %1=!%1:k=K!
SET %1=!%1:l=L!
SET %1=!%1:m=M!
SET %1=!%1:n=N!
SET %1=!%1:o=O!
SET %1=!%1:p=P!
SET %1=!%1:q=Q!
SET %1=!%1:r=R!
SET %1=!%1:s=S!
SET %1=!%1:t=T!
SET %1=!%1:u=U!
SET %1=!%1:v=V!
SET %1=!%1:w=W!
SET %1=!%1:x=X!
SET %1=!%1:y=Y!
SET %1=!%1:z=Z!
GOTO:EOF

B: Ülesanne: eraldada muutujast var kohas n k tähte.

Märkus:
Õnneks on .bat keeles võimalus kindlast positsioonist väja õngitseda mingi arv tähti.
Üldiselt mingeid muid võimalusi .bat –s sõnetöötluseks eriti ei ole. Kuid koos võimalusega asendada üks sõne teise sõne sees millegi muuga saab kõik sõnetöötlus algoritmid ise põlve otsas valmis ehitada ja seda ongi sageli Interneti avarustel tehtud.
Kui aga ei veel ole, saab seda ise teha, ajades vastava algoritmi kirjelduses näpuga järge.

Muutujast alamsõne eraldamise süntaks:

%var:~ < Mitu tähte muutuja algusest jätta välja> %
%var:~  < Mitmes täht, alatakse 0-st >,  < Mitu sümbolit > %

ehk:
%var:~n,k%

Teisisõnu, sõne tähed indekseeritakse alates indeksist 0 ning eraldatakse välja
k sümbolit alates positsioonist n.
%% asemel saab (ja on sageli väga vaja) kasutada !! märke.

Näiteid:
C:\bat>SET "kevade=Kui Arno isaga koolimajja saabus"

C:\bat>set arno=%kevade:~4,4%

C:\bat>set arno
arno=Arno

Negatiivne indeks tähistab lugemist sõne tagantpoolt ettepoole.
Ka k-d saab negatiivselt tõlgendada.
Kõigepealt eraldame sõnest välja alamsõne, alates positsioonist n (lugemist alustatakse 0-st).
Saadud sõnest aga eraldame välja kõik sümbolud KUNI positsioonini k.

C:\bat>echo %kevade:~-1%
s

C:\bat>echo %kevade:~-6,5%
saabu

C:\bat>echo %kevade:~-6,-1%
saabu

C:\bat>echo %kevade:~-10,-5%
jja s

Kui aga indeksid ise on .bat muutujad?

Sel juhul päästavad hädast välja HÜÜUMÄRGID (ja muidugi enabeDelayedExpansion)

C:\bat>set neli=4

C:\bat>set seitse=7

C:\bat>echo !kevade:~%neli%,%seitse%!
Arno is

Seda trikki kasutades saab kirjutada funktsioone, mis mingit sõnet täht – tähelt analüüsivad ja järelikult saab nii teoks teha kõik mõeldavad sõneanalüüsi algoritmid.

XI Miks me ikkagi armastame bat keelt, batmanid?

Lõpuks rauges ka minu hoog.
Esialgu sai ta toitu üllatusest ja ärritusest – kuidas tsüklist ei saa kuidagi välja, miks muutuja ei muutu, miks ei ole võimalik ennast programmeerimiskeeleks pidavas süsteemis normaalselt kommenteerida ...
Seejärel tekkis sportlik hasart – kas TÕESTI ei ole MS-s .bat keeles ühtegi kohta, kus ei esineks naeruväärsusi. Seni ei ole ma selliseid leidnud – alati leiate mõne põneva kala.
Oma kulutatud aja põhjenduseks oli vaja leida veenvaid valesid. Üheks selliseks sobis programmeerimiskeelte võrdlev analüüs. Heakene küll, otsime häid programeerimiskeeli või vähemalt rahuldavaid. Aga midagi võiks olla ka sellist, mis oleks peaaegu igas suhtes halb.
.BAT on selleks päris hea kandidaat.
Ka reaprogrammeerijad leiavad ennast sageli mingi funktsiooni süntaksi kavandamisel samasuguste valikute ees ja aeg ajalt oleks kasulik õppida ka sellest, mida EI TASUKS TEHA.

Teine argument, vahest ainukene, mis reaalselt õigustaks .bat keelele pühendatud aega asiste tööinimeste puhul – teil ei ole muud võimalust.

Kunagi elasime NSVL-s. Meil ei jäänud muud üle. Kui ei olnud mingit asja võimalik teha mõistlikul moel, tuli leida mittemõistlik viis millegi tegemiseks.
W administraatorid leiavad ennast sageli sellistest olukordadest. Teie töötuppa astuvad sisse mitmesuguse isikud, kes tahavad teie võrku kasutada ja teie hooldusteenust. Enamasti ei ole aega nende arvutitega liiga palju tegelda. Seal võib kohata igast ajastust pärit pille. Ja ainukene „keel“, mis kõigi W arvutite peal enam-vähem kindlasti jookseb, on .bat keel.
Ja isegi .bat keeles on võimalik automatiseerida tegevusi, mille peale muidu kuluks võib-olla tunde....

Ja lõpuks, nagu Tammsaarel Vargamäelgi juhtus – kui teha tööd ja näha palju vaeva, hakkab seda iselaadset arvutikeele paroodiat omamoodi armastama.
Mida selle teema kohta on teised arvanud, seda vaadake ja tundke vähemalt kaasa:
<8>


XII Viiteid.

Lingipuru:
Siit saate toitu veendumusele, et te ei ole oma palatis üksi, batmanid...

.BAT piibel, kõik teed lõpuks viivad selle raamatu poole. Ei ole hankinud, seetõttu soovitan.
Eestis ilmselt üheski raamatukogus ega riiulis ei leidu, kui hangite, olete üks esimesi.
Ei välista, et lähen ja tellin Krisost.
vt. veel 1-s viidatud raamatuid, kui tunnete ennast W -le alla jäävat skriptimisel.
Millegipärast ma kaldun arvama, et targem annabki järele...

Siin on kasulikku informatsiooni ja linke ka teiste W skriptimiskeelte kohta.
Teemasid:

Asendamatu koht, mitte ainult .bat keele kohta teadmiste hankimiseks.
Mina leidsin selle olevat kõige korralikuma omasuguste seas .bat käskude kirjeldamisel.

Väga hea DOS häkkerite kogunemiskoht.


Palju väärtuslikke praktilisi näiteid.


Siin on sadu kirjutisi erinevate W, Linux-i ja Mac „trikkide” kohta käsureal.
W, Linuxi administraatorile kindel asi, mida peab teadma või kohe läbi lugema ja siis teadma.


.BAT FOR käskude harjutamiseks käsureal väga asjalik abimaterjal.
Siin foorumis on tõesti toodud enamik vajaminevaid näpunäiteid .bat programmeerimisel.

Eksistentsiaalne lähenemine teemale.


Väga asjalik sissejuhatus .BAT -ga alustajatele, vahest üks parimaid.

Viited .BAT spetsiaalprobleemidega seotud infole:


9. FOR /F tsükli süntaks ning eol (kommentaari rea tunnusega seonduv):



10. TCC/LE cmd shell.



11. FOR /R: metasümbolite vajadus IN mallides



12. Spetsiaalsetega sümbolitega tegelemisest


b)

13. Uue rea sümboli saamisest spetsiaalsesse muutujasse.




14. Kuidas kommenteerida .BAT faile:

a) Kommentaaride süntaks.

Jätan endale vabaduse seda lingikogu võimalusel täiendada ja edasi kommenteerida.


Muud viited:


15.
K & R C keele piibel:


16. Dijkstra artikkel, kus ta määratleb COBOLI õpetamist kui kriminaalset tegu.
Dijkstra, COBOL


17. Dijkstra veel kuulsam artikkel, millest enamasti teatakse vaid pealkirja:
Goto considered harmful


18. Wiki ülevaade võrdlusoperaatoritest, näete, et BAT on täiesti eriline siin


19. Vähima üllatuse printsiibist Ruby loojalt Yukihiro Matsumotolt: