Jeg tror at det er meningen at der er nogle der kommer med tips til hvordan man kan løse opgaven, og at man derefter selv prøver at lave et script, man kan så poste et indlæg her hvis man har problemer, eller poste en løsning. Meningen er ikke så meget at få løst problemet, som at finde ud af hvordan man løser problemet.
For at løse denne opgave skal man vide at man kan regne i bat scripts vha. kommandoen "set /A" og at man kan liste alle filer i en mappe vha. kommandoen "for" Hvis man skriver "help set" og "help for" i en kommando prompt får man at vide hvordan man bruger disse kommandoer. Specielt bør man kikke på det der står til sidst om kommandoen for angående %~zI
Synes godt om
Slettet bruger
08. september 2003 - 20:01#3
Ah, ja så blev jeg så klog :-) Jeg ventede egentlig mere da der stod "Vi starter med at lave en bat-fil fra bunden".
Hvis du vil starte på et endnu lavere niveau, lære om parametre, grundlæggende bat-fil teknikker, osv, så kan det anbefales at slå op under batch i Start->Help.
Det skal vist lige nævnes at man nok ikke slipper for at kikke på call "kommandoen", og muligheden for at kalde en "funktion" med "call :func", der sansynligvis skal bruges sammen med for. Og så skal man også lige vide at man returnerer fra en "funktion" ved at skrive goto :eof, men det finder man ud af hvis man skriver "help call".
Jeg har lige kigget lidt på de forskellige kommandoer. Jeg synes det er lidt svært, at læse om de forskellige kommandoer i cmd. Er det de samme oplysninger der ligger i Start->Help? Eller har microsoft en oversigt over de forskellige kommandoer?
Den information du får ved at skrive "help for" er stort set den samme som du får under Start->Help. Der er dog lidt flere informatiner under Start->Help.
Når jeg laver bat-scripts starter jeg i reglen med at lave noget meget simpelt, det kunne i dette tilfælde være:
for %%I in (*) do echo %%I
Hvilken lister alle filer i nuværende direktorie.
Hvis man skriver:
for %%I in (*) do echo %%I %%~zI
får man, udover filnavnet på alle filer, deres størrelse.
Næste skridt er at kalde en "funktion" fra for løkken:
for %%I in (*) do call :add %%I %%~zI goto :eof
:add echo %1 %2 goto :eof
Det der sker her er at funktionen (add) bliver kaldt med parametrene %%I (dvs filnavnet) og %%~zI (dvs størrelsen på filen) for hver fil i mappen. Disse parametre bliver i funktionen til %1 og %2. Den første "goto :eof" afslutter bat-filen, den anden returnerer fra funktionen.
Nu mangler man blot at lægge alle fil-størrelserne sammen.
Jeg tror at jeg forstår det, men alligevel ikke....
Kan du ikke forklare, det engang til helt fra starten? Forklar lige hvad de forskellige ting gør i kommandolinien. Jeg har kigget i Start->Help, men synes lige, at jeg kører lidt rundt i det.
FOR, IN og DO skal være der i alle for løkker og kan ikke ændres. Alle for loops specificerer en variabel, et set og en kommando. Nogle for loops har paramtre, den du nævner har ingen.
Det for kommandoen gør er at køre "command" for hver element i "set" på en sådan måde at "variabel" bliver sat til at være det næste element i "set".
I "for %%I in (*) do echo %%I" er variablen %%I, set er *, kommando er "echo %%I", og et element i set er en fil.
Så man kan forklare kommandoen som: For hver fil udfør kommandoen "echo %%I" idet %%I erstattes med filnavnet.
En lille detalje: Variablen er egentlig %I og ikke %%I. Grunden til at vi skriver %%I er at %% bliver erstatet med % når kommandoen parses, kommandoen for ser parametren som %I. Hvis man vil køre kommandoen fra kommandolinien skal man skrive: for %I in (*) do echo %I
Jeg har siddet og lavet lidt med bat-scriptet. Jeg kan se, at den ligger tallene sammen. Jeg har lavet en "pause" kommando sidste i scriptet, så jeg kan se, hvad det gør. Den skal vel vise resultatet inden cmd prompten lukker ned. Hvordan gør jeg det?
set/a sum=0 for %%I in (*) do call :add %%I %%~zI goto :eof
I tidernes morgen (dvs før Windows NT) brugte man call kommandoen til at kalde et batch script fra et andet. Senere fandt man på at det kunne være smart at to eller flere batch scripts kunne være i samme fil, og ikke som førhen i forskellige filer. Derfor opfandt man "call :label" syntaxen. Samtidig opfandt man "goto :eof" syntaxen til at "returnere" fra et batch-script før man når enden af filen, eof betyder netop End Of File.
Du kan derfor betragte scriptet ovenfor som to seperate scripts, f.ex sum.bat:
set/a sum=0 for %%I in (*) do call add.bat %%I %%~zI
og add.bat:
echo %1 %2 set/a sum+=%2 echo %sum% pause
Det der sker i sum.bat er at den sætter sum til 0 og kalder add.bat for alle filer i nuværende direktorie.
Add.bat udskriver de to første parametre (%1 og %2) og lægger %2 til sum, samt udskriver sum og kalder pause.
Det vi gerne vil er at udskrive summen når vi har lagt alle størrelserne sammen, da add.bat bliver kaldt for hver fil, bør det være klart at vi skal udskrive summen i sum.bat der kun bliver udført en gang.
Så vi skal have flyttet:
echo %sum% pause
Til sum.bat
Da vi ønsker at have det hele i én fil, kommer resultatet til at se sådan ud:
set/a sum=0 for %%I in (*) do call :add %%I %%~zI echo %sum% pause goto :eof
:add echo %1 %2 set/a sum+=%2
Det bemærkes at "goto :eof" er afslutningen af den del vi før kaldte sum.bat.
Der lige et par småting det kunne være rart at få rettet/tilføjet til scriptet inden vi går videre:
1: Det er vel ikke nødvendigt at udskrive alle filenavne og deres størrelse. 2: Det ville være rart at få talt hvor mange filer der er i mappen. 3: Det kunne være rart med en lidt mere forklarende text som output f.ex: Der er fundet xxx bytes i yyy filer.
1: Slet den linie der udskriver filnavn og størrelse for hver fil. 2: Sæt en variabel, f.ex antalfiler til 0, tæl den en op (set /A antalfiler+=1) for hver fil, udskriv den. 3: Lav linien der udskriver om så den skriver en mere forklarende tekst (inklusive antallet af filer).
Når man tester batch-scripts er det altid en god ide at åbne en kommando-prompt (Start->kør/run) og så køre kommandoen derfra.
Hvis du vil have et batch-script til at stoppe (f.ex før det afslutter kan man indsætte en pause kommando).
Hvis du kikker på output fra scriptet kan du se at det kører :vis funktionen for alle filer, hvor vi kun ønsker at det skal gøre det en gang til slut. Det gør den fordi den kalder funktionen :add for hver fil og den returnerer først fra denne funktion når den når bunden af filen dvs. efter :vis
Man vil også kunne se at den kører funktionen :add lige efter den har udført for kommandoen fordi denne kommer lige efter for kommandoen.
Du er måske nød til at fjerne cls kommandoen for at kunne se hvad der sker. Det kan også hjælpe at øge antallet af linier man kan se i vinduet (Alt+Mellemrum), så du kan scrolle tilbage og se hvad der sker.
Så vi (dvs du) skal have den til at returnere fra :add funktionen, inden den når :vis, det gør man med goto :eof
Du skal også have den til at udføre funktionen :vis efter for kommandoen, det gør du med goto :vis Den vil så afsluttte herefter, da du ikke har kaldt :vis men kun hoppet til den.
Når man kalder et bat-script eller en label vha call :label bliver den første parameter man angiver til %1, den næste til %2 osv, man adskiller parametre med mellemrum.
Når du kalder lablen :add med f.ex test.dat og 12 bliver %1 i funktionen :add til test.dat og %2 til 12.
I for loopen bliver %%I erstattet med filnavnet og %%~zI med størrelsen på filen. Det er fint sålænge filen hedder f.ex test.dat, men hvis filen hedder: fil navn med sjove mellemrum . ext har vi et problem, for så bliver %1 i :add til fil og %2 bliver til navn (og %3 til med osv), og det går jo ikke.
For at undgå dette sætter vi "" omkring %%I for så bliver det til en parameter. I :add vil man så se %1 som "fil navn med sjove mellemrun . ext" (inklusive "")
Hvis man vil have fjernet disse "" fra %1 skriver man %~1
AD 1: Du skal sætte "" omkring %%I. Prøv at udskrive filnavnet med og uden %~1
AD 2: Hvis du fjerner %%I (eller "%%I") i kaldet til :add bliver størrelsen af filen %1 i :add, så :add skal ændres så den lægger %1 til størrelsen.
Næste trin kunne være at udvide scriptet til at finde antal filer og deres samlede størrelse i en bestemt mappe f.ex. c:\winnt, mappen skulle så angives som parameter til scriptet.
Du skal ændre det set som for kommandoen looper over. Indtil nu har vi brugt * som set, hvis vi ændrer * til *.foo vil scriptet tælle størrelsen på alle .foo filer, og hvis vi ændrer set til c:\win\* vil den finde størrelsen for alle filer i c:\win
Den første parameter man angiver til et script hedder %1 (ligesom i funktioner).
Det vil måske være en ide at checke om brugeren har husket at angive en sti, det gør man med kommandoen if, f.ex
if {%1}=={} ( echo Du skal angive en parameter goto :eof )
Hvis brugeren ikke angiver nogen sti kunne man bruge nuværende direktorie som default, i stedet for at returnere.
Ad 2: Du skal have to for løkker, en der kører når if {%1}=={} med * som set og en med %1\* som set. Disse skal placeres i if () else () som vist i kommentaren fra 27/09-2003 00:11:34 i stedet for "Køre for loopen ..."
At ændre uskriften kan gøres vha. set /A Du kan også bruge set direkte, %sum:~-3% giver således de sidste 3 cifre i sum, %sum:~0,-3% giver alt i sum pånær de sidste tre cifre. Start med at lave algoritmen på et stykke papir, og lav så koden.
Prøv at vise hvad du har lavet med de to for løkker. Jeg synes ikke jeg kan komme med flere hints, uden at komme med en løsning.
Når man skal lave et program starter man (specielt hvis man er ny og/eller opgaven er stor) med at lave en symbolsk repræsentation af programmet. Ved større opgaver vil man ofte bruge et eller andet grafisk værktøj, til vore (relativt) simple scripts kan vi lave en tekstuel beskrivelse af programmet. Hvis man skal forstå hvad et program gør kan man starte med at lave en symbolsk repræsentation af programmet (det kaldes reverse engeneering).
Man kan f.ex "oversætte":
if {%1}=={} ( echo Du skal angive en parameter goto :eof )
Til:
Hvis DenFørsteParameter er tom så: Udskriv "Du skal angive en parameter" Afslut END.
Bemærk indryk bruges til at beskrive at dette er en blok. Hvordan man laver "sproget" er ikke så vigtigt, det skal blot være en mellemting mellem almindelig dansk og det sprog man vil programere i.
Man kan oversætte følgende: for %%I in (*) do call :add %%~zI Til: For hver fil (*) kald :add med størrelsen på filen (%%~zI) som parameter.
Hvis du oversætter hele scriptet på den måde vil jeg tro at du kan se fejlene. Hvis ikke så fjern første linie (@echo off) og kør så scriptet i en mappe med kun nogle få filer og se hvad der sker. Du kan evt. køre scriptet sådan her:
tt.bat *.ght >tt.txt
Hvor tt.bat er navnet på bat-filen og tt.txt er en fil hvori output fra kommandoen bliver puttet, så kan du åbne tt.txt i f.ex. notepad og se det der.
Med hensyn til udskriften, kan vi lave en symbolsk beskrivelse der ser sådan ud:
Tal=ABCDEFGHIJ (DVS tal er det indput der skal laves om til A.BCD.EFG.HIJ) One=TreSidsteCifreI(Tal) FjernTreSidsteCifreI(Tal) Kilo=TreSidsteCifreI(Tal) FjernTreSidsteCifreI(Tal) Mega=TreSidsteCifreI(Tal) FjernTreSidsteCifreI(Tal) Giga=Tal Udskriv: Giga.Mega.Kilo.One
Man kan så oversætte:
Mega=TreSidsteCifreI(Tal)
til:
set Mega=%Tal:~-3%
På samme måde kan man oversætte:
FjernTreSidsteCifreI(Tal)
Til:
set Tal=%Tal:~-0,-3%
Og til slut kan man oversætte:
Udskriv: Giga.Mega.Kilo.One
Til
echo %Giga%.%Mega%.%Kilo%.%One%
Der er dog en lille detalje. Det er ikke altid at Tal indeholder 10 cifre, så vi er nødt til at tage højde for dette, pseudo koden vil så komme til at se sådan ud:
Tal=ABCDEFGHIJ (DVS tal er det indput der skal laves om til A.BCD.EFG.HIJ) One=TreSidsteCifreI(Tal) FjernTreSidsteCifreI(Tal) Hvis Tal er tom så: Udskriv One Afslut End Kilo=TreSidsteCifreI(Tal) FjernTreSidsteCifreI(Tal) Hvis Tal er tom så: Udskriv Kilo.One Afslut End Mega=TreSidsteCifreI(Tal) FjernTreSidsteCifreI(Tal) Hvis Tal er tom så: Udskriv Mega.Kilo.One Afslut End Giga=Tal Udskriv: Giga.Mega.Kilo.One
Jeg vil foreslå at du laver et selvstændig script, som du giver det tal der skal udskrives til som parameter. Du kan så putte det ind i det andet script når du har fået det til at virke.
Jeg har kigget lidt på det sidste og synes, det er lidt svært. Enten skal jeg have lidt mere hjælp ellers synes jeg bare, at vi skal droppe det sidste.
Vi kunne evt. starte på backup/restore scriptet. Scriptet skal kopiere filer/mapper eller evt. et helt drev over til et andet sted. Hvis det er muligt kunne vi tilføje en logfil, og en progressbar der viser hvor lang processen er kommet.
Jeg opretter selvfølgelig et nyt spørgsmål, hvis du har mod til at vi laver backup/restore scriptet nu.
Jeg går ud fra at du med "det sidste" mener at lave 12345678 om til 12.345.678. Jeg har lavet en start på sådan et script, men det håndterer kun tal op til 999999, så du kan prøve at udvide det til at håndtere tal op til 9999999999.
@echo off if [%1]==[] echo Du skal angive et tal & goto :eof set tal=%1 set one=%tal:~-3% set tal=%tal:~0,-3% if [%tal%]==[] echo %One% & goto :eof
set kilo=%tal% echo %kilo%.%one%
Jeg vil gerne hjælpe med at lave et backup/restore script, men jeg tror at det vil være for svært for dig endnu. Jeg tror at der er for mange helt grundlæggende ting du skal have lært før vi kan starte på det. Mens du får ovenstående til at spille kan jeg prøve at finde nogle overkommelige øvelser.
Hvor komplext et backup/restore script kan være, kan du se her:
Vi kan godt lave et backup/restore script med logfil senere, men det er rarest at vide nogenlunde hvad man gør inden man begynder at flytte rundt på sine (og andres) filer.
Jeg kender ingen måde til at lave en progressbar fra en bat-fil. Man kan kun udskrive text, en linie af gangen.
Nogle forslag til øvelser: 1: Lav et script der spørger om hvad du hedder og derpå udskiver Hej "navn"
2: Udvid/ændr scriptet fra denne øvelse til at: 2a: Liste alle filer over en given størrelse 2b: Finde den største fil 2c: Finde filer der ikke er fra i år.
3: Lav en menu, der f.ex ser sådan ud: Vælg: A: Hallo B: Goddag C: Hej Hvor brugeren så skal vælge A, B eller C ved at taste A/B/C, hvorpå scriptet udskriver hvad brugeren har valgt. Husk at checke om brugeren har tastet noget ugyldigt.
Nu har jeg forsøgt, men jeg er ikke helt færdig. Jeg har i første omgang kun lavet "disse filer fylder". Scriptet skriver hvor meget filer fylder, men når jeg angiver en anden sti, fylder filerne stadigvæk det samme. Hvad gør jeg forkert?
@echo off set/a tal=0 set/a antalfiler=0 if {%1}=={} ( echo Du skal angive en parameter goto :eof )
if {%1}=={} ( for %%I in (%1\*) do call :add %%~zI ) else ( for %%I in (*) do call :add %%~zI )
set one=%tal:~-3% set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %One% & goto :eof set Kilo=%tal:~-3% set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %Kilo%.%One% & goto :eof set Mega=%tal:~-3% set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %Mega%.%Kilo%.%One% & goto :eof set Giga=%tal:~-3% set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %Giga%.%Mega%.%Kilo%.%One% goto :eof
Følgende liner skal væk, da du jo netop tillader at man undlader at angive en parameter:
if {%1}=={} ( echo Du skal angive en parameter goto :eof )
Testen i følgende vender omvendt, vi skal netop liste filer i %1\* hvis ikke %1 er tom
if {%1}=={} ( for %%I in (%1\*) do call :add %%~zI ) else ( for %%I in (*) do call :add %%~zI )
Så du skal lave det om til:
if {%1}=={} ( for %%I in (*) do call :add %%~zI ) else ( for %%I in (%1\*) do call :add %%~zI )
Det er grunden til at det altid skriver det samme, for det vil altid tælle summen for alle filer i * (nuværende mappe), og ikke den mappe du angiver som parameter.
En lille detalje, i slutningen af din formatering af størrelsen har du: set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %Giga%.%Mega%.%Kilo%.%One%
Hvor stor kan summen af alle fil størrelser blive? Vil tal ikke altid blive sat til at være en tom streng her og testen således altid være sand?
Nu kører det (næsten). Scriptet laver en lille fejl "Disse filer fylde .491.611.734". Der skal selvfølgelige ikke stå punktum foran et tal. Jeg kan ikke lige selv se fejlen.
@echo off set/a tal=0 set/a antalfiler=0
if {%1}=={} ( for %%I in (*) do call :add %%~zI ) else ( for %%I in (%1\*) do call :add %%~zI )
set one=%tal:~-3% set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %One% & goto :eof set Kilo=%tal:~-3% set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %Kilo%.%One% & goto :eof set Mega=%tal:~-3% set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %Mega%.%Kilo%.%One% & goto :eof set tal=%tal:~-0,-3% if [%tal%]==[] echo Disse filer fylde %Giga%.%Mega%.%Kilo%.%One% goto :eof
Problemet er at når man regner vha. SET /A kan man ikke bruge tal større end 2147483647. Så hvis man har et drev med mere end 2G dur metoden ikke :-(
Prøv at køre kommandoen: set /A 2147483647 + 1 i en kommando prompt og se hvad resultatet bliver.
Vi kan nu vælge at sige at "det er der ikke noget at gøre ved" eller finde en metode til at afhjælpe det problem. Det sidste er ikke helt let, men det er op til dig om du vil arbejde videre med det eller noget andet.
Du får lige nogle tips til hvordan du kan lave det så den kan regne med tal større end 2G.
Du skal bruge to variabler, en der tæller i giga bytes og en der tæller resten. Vi kan kalde de to variabler for giga og resten. (Vi siger at 1 giga byte er 1 000 000 000 bytes her, andre vil mene at 1 Giga byte er 1024*1024*1024 bytes.) Husk at sætte resten og giga til at være 0 når du starter.
Når du skal lægge et nyt tal til giga og resten (i :add) skal du starte med at dele tallet op i giga og resten, det gør du ved at tage de sidste 9 cifre for sig og de øverste cifre for sig. Så lægger du de to dele til giga og resten Til slut ser du om resten er blevet større end 1000000000. Hvis den er det lægger du 1 til giga og trækker 1000000000 fra resten.
Når du skal udskrive kan du sætte giga og resten sammen til et tal, det gør du sådan:
set tal=%giga%%resten%
Hvilket er fint medmindre at:
1: giga er 0, for så vil tal blive til 0123456789, hvis resten er 123456789 2: resten er f.ex 234, for så bliver tal 1234 hvis giga er 1 (hvor de skulle være 1000000234
Så vi bliver nødt til at tage hensyn til disse tilfælde, hvilket i pseudo sprog kunne se sådan ud:
Hvis giga er 0 set tal=resten, done Set 9 0'er foran resten (set resten=000000000%resten%) set resten til at være de 9 sidste cifre i resten set giga og resten sammen til tal, done
Jeg har kigget på det, men synes det ser lidt svært ud. Jeg vil gerne lave det på et tidspunkt, og så om jeg selv kan løse opgaven. Kan vi ikke lade opgaven ligge lidt?
Jo, det er vist på tide at starte på en ny opgave. Har du tænkt over hvad du vil lave? Ellers kommer der et par forslag mere:
1: Lav et bat-script der kopierer billeder fra et digital kamera til harddisk. Det skal lave en mappe med dags dato som navn og kopiere billderne til denne mappe.
2: Lav et script der rydder op i en mappe med billeder, den skal lave under-mapper ud fra datoerne på billederne og flytte billederne til disse mapper.
3: Lav et script der giver filer navne efter den dag de er gemt.
Skal jeg bare oprette et spørgsmål under StyreSystemer - Workstation - DOS og kalde spørgsmålet Bat-script? Hvad skal jeg skrive af tekst til spørgsmålet?
Jeg tror at det vil være en god ide at oprette spørgsmålet i DOS kategorien. Beskriv opgaven som du har valgt og beskriv hvad sprørgsmålet går ud på (at det er et spørgsmål om at lære at lave bat-scripts). Da de fleste ting der bruges i disse scripts ikke virker på Win95/98/ME, vil jeg foreslå at du gør opmærksom på det. Lav evt. en henvisning til dette spørgsmål. Du kan kalde spørgsmålet "Bat programmering del 2" da dette jo er del 1. Jeg synes at det ville være rart hvis vi kunne lokke nogle flere til at deltage.
Der er en anden som også har spurgt om på progressbar, her er på eksperten.dk (du har selv deltaget i spørgsmålet): http://www.eksperten.dk/spm/415173
Hvis man ikke kan lave en progressbar, kan man så ikke bare lave en status over, hvor stor pct. vis der er kopieret? Hvis man ved hvor meget alle filerne fylder, kan man vel beregne hvor meget, hvor meget der er kopieret af filer?
Eks. Status 1205422/17503150 og så regne det pct. vis ud (her 6,9%)
Når man laver de fire bootdisketter under windows 2000 bruger man makeboot.exe filen til at lave disse disketter (makeboot-filen ligger på windows 200 cd´en). Makeboot-filen viser, hvor mange pct. der er kopieret ned på disketten. HVordan gør den det???
Man kan godt lave et program (en .exe, makeboot.exe er et program) i f.ex. C++ eller VB, der laver en "progress bar" mens den kopierer, det er ikke særlig svært.
Men man kan ikke gøre det med de programmer der ligger i dos/windows.
Man skal også lave et program (en exe) for at få en progress bar i en prompt.
Man kan lave en progressbar i mange sprog (men ikke batch), jeg kender mest til C/C++.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.