Dette spørgsmål er til at hjælpe og komme med tips til, hvordan man laver disse bat-scipts:
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.
Disse scripts laves til windows2000 (de fleste ting der bruges i disse scripts ikke virker på Win95/98/ME)
I dette særtema ser vi på, hvordan cloud og AI bliver fundamentet for virksomhedernes digitale forretning, og hvordan de nye muligheder for automatisering og forretningsværdi kan udnyttes uden at miste overblik, sikkerhed og menneskelig kontrol.
Hvis man har billeder på kameraet til at ligge i g:\hh\ og vil have dem til at ligge i en ny undermappe, der skal hedde sommer, under c:\kamera, kan man lave følgende i en bat-fil:
md c:\kamera\sommer copy g:\hh\* c:\kamera\sommer
Hvis man nu vil have mappen til at hedde "to 23-10-2003" (fordi det er dagen i dag), kan man bruge %date% i stedet for sommer i ovenstående. date er en slags variabel der altid er dags dato. Normale variable kan ses vha. set kommandoen, det kan man ikke med date. Man kan heller ikke ændre %date% med set kommandoen som man kan med andre variable.
Nu er der jo nok en eller anden der spørger hvilken forskel der er på date og %date%, svaret er at date er navnet på variablen og %date% er indholdet af variablen.
En eller anden vil sikkert ønske sig at give mappen navnet "23-10-2003", dvs uden ugedag, så den må vi lige have fjernet. Det kan man gøre på mange måder, f.ex. kan man fjerne de først 3 tegn i %date%, det vil komme til at se sådan ud: %date:~3%. Hvis ikke det står helt klart hvorfor, vil det være en god ide at køre kommandoen "help set" i en kommando prompt (eller kikke efter set i Start -> Help). For at se at det virker kan man køre kommandoen "echo %date:~3%" i en kommando prompt. Så for at få mappen til at hedde 23-10-2003 erstatter man blot sommer med %date:~3% i eksemplet ovenfor.
Det kunne også tænkes at nogen ville ønske at kalde mappen for 23102003, dvs fjerne både ugedag og -
Det kræver at vi gør det i to trin, først opretter vi en variabel kaldet dato, der er %date% uden ugedag:
set dato=%date:~3%
Dernæst fjerner man - fra dato, det gøres igen med set kommandoen:
set dato=%dato:-=%
Hvilket kan oversættes til: "set dato til at være dato hvor alle - er sat til at være ingenting".
Nu putter man blot de to sidste kommandoer ind før kommandoerne fra starten og erstatter sommer med %dato% så har man løst den første opgave, ganske simpelt.
Skulle der være enkelte der stadig bruger NT vil de sikkert vide (ellers vil de opdate det) at der ikke en %date% environment variabel i NT (den blev opfundet med 2000). Så de er nødt til at lave den, hvilket kan gøres med følgende simple kommando:
for /F "tokens=*" %%I in ('date /T') do @set date=%%I
Hvis du synes at det ser lidt indviklet ud, er det nok en god ide at køre kommandoen "help for". Som vanligt med for løkker skal %%I erstattes med %I i ovenstående hvis det skal køres direkte fra kommando linien.
Det bør lige bemærkes at hvis man har mellemrun i filnavne og/eller stier er et nødvendigt at sætte "" omkring. Det kunne f.ex være tilfældet hvis man vælger at bruge %date% direkte.
De to linier fra starten bør derfor se ud som følger:
For at løse opgave 2 og 3 vil det være praktisk at starte med at lave en løkke der kalder en funktion for hver fil i mappen. Det vil se nogenlunde såden ud:
for %%I in (*) do call :do_it "%%I" goto :eof :do_it echo %~1
I korte træk er det der sker her, at man kalder funktione :do_it for hver fil i mappen, filnavnet er den første parameter (%1) i :do_it. DVS. at %%I i for loopen bliver til %1 i :do_it. Funktionen :do_it udskriver blot filnavnet. Jeg kaldte funktionen med "%%1", så navnet vil have "" omkring i funktionen, disse kan man fjerne ved at skrive %~1 i stedet for %1. Dette gøres for at kunne håndtere filnavne med mellemrum. Hvis man kun ønsker at se på f.ex. jpg-filer ændres * i for-loopen til *.jpg
Hvis det stadig ikke står klart hvordan dette virker, kan det anbefales at læse om kommandoerne call og for. Eller studere lektion 1, hvor metoden bliver brugt en del.
I funktionen :do_it har vi brug for kende tiden på filen. Da du læste om call og/eller for så du måske at det kunne man gøre ved at kalde :do_it med "%%~tI" eller ved at bruge %~t1 i funktionen, vi vælger den sidste metode.
Vi kan nu udvide scriptet fra før så det udskriver både tiden og filnavn, dette gøres ved at ændre sidste linie til:
echo %~t1 %~1
Det vil i nogle tilfælde se sådan ud: 01-10-03 00:58 www.cmd
Det ses heraf at %~t1 giver både dato og klokkeslet.
Vi kan nu sætte en variabel til at være tiden på filen:
set dato=%~t1
Man kan nu manipulere med %dato% som i mit forrige indlæg og bygge den kommando der skal til for at løse hhv opgave 2 og 3.
Jeg vil stadig gerne have dig til at forklare hvad du forestiller dig scriptet gør.
Du har en linie der ser sådan ud:
set dato=%~t1
Hvad gør den linie?
Har du prøvet at køre scriptet? Hvad sker der når du kører det?
I indlægene fra "23/10-2003 23:55:27" og "24/10-2003 19:20:10" beskriver jeg hvad der skal til for at løse opgave 1. Der er ikke brug for nogen for-løkke for at løse opgave 1 (medmindre du er begyndt at bruge NT).
For at løse opgave 2 og 3 skal der bruges en for-løkke, men lad os starte med at løse opgave 1.
Scriptet fra 24/10-2003 19:56:19 viser de filer som ligger i biblioteket (kun filnavn). Hvis man så tilføjer linien "set dato=%~t1" til scriptet, så udskriver scriptet også tid og dato ud for filnavnet.
Jeg starter med opgave 1 og læser lige dit indlægget fra 23/10-2003 23:55:27 igen.
Du skal erstatte sommmer med en formateret version af %date% som beskrevet "23/10-2003 23:55:27" Da du jo ikke vil have alle billeder til at ligge i "e:\kamera\sommer", men f.ex i "e:\kamera\30102003" hvis det er billeder fra i dag.
Du kan få en glimrende forklaring på copy og xcopy ved at køre kommandoerne "help copy" og "help xcopy" eller kikke i Help->Start.
Ja. Hvordan ser det ud når du kører scriptet? Hvad sker der hvis du kører det flere gange på en dag? Det var måske en ide at check om mappen eksisterer inden du opretter den? (prøv at kikke under if kommandoen og se på if exist ...)
Jeg har ikke lige fået oprettelse af mappen på plads. Jeg får stadigvæk:
Der findes allerede en undermappe eller en fil med navnet e:\kamera\04-11-2003.
set dato=%date:~3% if exist "e:\kamera\%dato%" echo Mappen findes if not exist "e:\kamera\%dato%" echo Mappen findes ikke md "e:\kamera\%dato%" copy "o:\DCIM\100MSDCF\*" "e:\kamera\%dato%"
set dato=%date:~3% if exist "e:\kamera\%dato%" echo Mappen findes if not exist "e:\kamera\%dato%" md "e:\kamera\%dato%" copy "o:\DCIM\100MSDCF\*" "e:\kamera\%dato%"
Man kan f.ex tilføje >logfile.txt efter copy kommandoen, så skriver den til logfile.txt i stedet for til skærmen. Man kan kan også skrive direkte til logfilen vha, f.ex:
echo Opretter mappen %dato% >> logfile.txt
Hvis man bruger > overskrives filen, hvis man bruger >> tilføjes til filen.
Man kan også lave det så der kun skrives hvor mange filer der er kopieret ved at bruge kommandoen findstr.
Hvis jeg vil kopiere alle *.jpg filer til mappen c:\dump bruger jeg:
copy *.jpg c:\dump
copy vil så udskrive (hvis din windows snakker engelsk): DSC00001.jpg DSC00004.jpg DSC00005.jpg 4 file(s) copied.
Hvis jeg kun vil have fat i linien "4 file(s) copied." kan jeg bruge findstr som et filter:
copy *.jpg c:\dump |findstr copied
| er ALTGR+"tasten til venstre for BackSpace", og betyter pipe, dvs. overfør output fra kommandoen til venstre til kommandoen til højre.
Vil man så udskrive linien til logfile.txt bliver hele kommandoen: copy *.jpg c:\dump |findstr copied >logfile.txt
Hvis man kun vil have fat i tallet må man kombinere med en for-løkke, men så er vi ovre i det mere avancerede (kun for dem der vil have 13).
Som du kan se er der mange muligheder, så prøv at lege lidt med det, og fortæl evt. hvad du kunne tænke dig at putte i log-filen.
Jeg tænkte på, kan man bruge "copy *.jpg c:\dump" til at finde alle jpg-filer på flash-kort? Billederne ligger "o:\DCIM\100MSDCF\*", men det er jo ikke sikkert, at det gør det hvis jeg låner et kort fra et andet kamera.
Tilbage til log-filen. Jeg kunne godt tænke mig noget i denne stil, hvis man kan lave det:
1) Hvornår kopieringen har fundet sted 2) Hvor billederne er kopieret fra og hvorhen til 3) Evt. en status over hvor mange filer der er kopieret og evt. om der er nogle fejl.
Start med at oversætte opgaven til pseudo sprog, kan evt gøres i flere step:
Step 1: For hver mappe, kopier alle filer i mappen til destinationen.
Step 2: For hver mappe kald funktionen ProcessFolder, angiv mappens navn som parameter I funktionen ProcessFolder; kopier alle filer i mappen (parameteren) til destiantionen.
Step 3: For Hver Undermappe, Kald :ProcessFolder MappeNavn Done
:ProcessFolder For hver fil i MappeNavn, Kopier filen til destinationen Done
Nu skulle det være forholdsvist enkelt at oversætte pseudo-koden fra step 3 til batch kommandoer. De to For sætninger skal blive til en for-løkke og Done kan direkte oversættes til "goto :eof"
Når du har fået scriptet til at kopiere filerne, kan du tilføje skrivning til en logfil. Når du skal i gang med det, starter du med at skrive i pseudo-sprog hvad du vil have tilføjet hvor. Derefter oversætter du pseudo-sproget til batch-kommandoer.
Step 1 til 3 er forskellige beskrivelser på det samme problem. Det var tænkt at step 1 skulle være det der lignede almindelig dansk mest og step 3 det der ligner batch-script mest.
Det der i step 3 beskrives som:
"For Hver Undermappe, Kald :ProcessFolder MappeNavn"
kan laves ved at bruge:
for /F %I in ('dir /S /B /AD o:\') do @echo %I
Og kalde :ProcessFolder med mappennavnet (%I) i stedet for at udskrive mappe navnet.
Linien:
For hver fil i MappeNavn, Kopier filen til destinationen
Skal ligeledes oversættes til en for-løkke.
Husk at når man bruger for-løkker i batch-scripts skal alle %I være %%I
Det jeg ville var, at du skulle lave et script der gør følgende:
For Hver Undermappe, Kald :ProcessFolder MappeNavn Done :ProcessFolder For hver fil i MappeNavn, Kopier filen til destinationen Done
Kommandoen: for /F %I in ('dir /S /B /AD o:\') do @echo %I
Udfører noget der kan beskrives som:
For Hver Undermappe, Udskriv MappeNavn
Du skal ændre kommandoen så den kalder en funktion der hedder :ProcessFolder i stedet for at udskrive mappenavnet. At kalde en funktion der hedder :kurt gøres med kommandoen call :kurt Du skal overføre mappenavnet til funktionen, det gøres ved at angive mappenavnet efter navnet på funktionen. I dette tilfælde er mappenavnet %I (/%%I)
Dernæst skal du lave funktionen :ProcessFolder Du kan starte med at lade :ProcessFolder udskrive mappenavnet. I :ProcessFolder er mappenavnet %1
Når du har det til at virke, kan du lave en for-løkke der udfører "For hver fil i MappeNavn, Kopier filen til destinationen".
Nu går det i den rigtige retning. Dog kopiere scriptet billederne to gange.
Først ligger billederne i denne sti "E:\kamera\18-11-2003", derefter ligger de også i "E:\kamera\18-11-2003\100MSDCF". Så laver scriptet den fejl, at den kopiere alle filer og mapper fra mappen kamera til roden af mit c-drev.Hvad gør jeg forkert?
set dato=%date:~3% if exist "e:\kamera\%dato%" ( echo Mappen findes ) else ( md "e:\kamera\%dato%" )
for /F %%I in ('dir /S /B /AD o:\') do call :ProcessFolder %%I :ProcessFolder xcopy/s %1 "e:\kamera\%dato%"
xcopy er et program for folk der bruger win9x/ME. Brug copy i stedet, eller find en kommandolinie parameter til xcopy der fortæller at destinationen er en mappe.
Ja, sådan. Nu kører det!. Jeg har tilføjet en log-fil. Jeg ved ikke, hvad vi mere kan lave på dette script. Nogle gode idéer eller skal vi stoppe her med dette script?
set dato=%date:~3% if exist "e:\kamera\%dato%" ( echo Mappen findes ) else ( md "e:\kamera\%dato%" )
for /F %%I in ('dir /S /B /AD o:\') do call :ProcessFolder %%I goto :eof :ProcessFolder xcopy/i/y %1 "e:\kamera\%dato%" >log.txt
Der behøves vist ikke mere til dette script, men du kunne overveje forskellen på: >log.txt og >>log.txt
Du kan også prøve at få xcopy til at fejle og se hvad der sker. Du kan f.ex prøve at kopiere filerne to gange, og måske lave filerne read-only (attrib +R *.*) efter første gang der er kørt.
for /F "delims=;" %%I in ('dir /S /B /AD') do call :ProcessFolder "%%I" goto :eof :ProcessFolder dir /B "%~1\*.jpg"
Bemærk at jeg har tilføjet "delims=;" til for-løkken og ændret den sidste %%I til "%%I". Dette er gjort for at forhindre den i at splitte stier med mellemrum i navnet op i flere dele. Dette virker fordi mappe og fil navne ikke kan indeholde ; i dos/windows.
I ProcessFolder er %1 mappenavnet med "" omkring, %~1 er mappe navnet uden "". dir kommandoen i ovenstående vil liste alle .jpg-filer i mappen, men uden stien (dvs DSC00123.jpg og ikke o:\whatever\DSC00123.jpg). Vi vil gerne have puttet stien på, det kan vi gøre ved at putte det ind i en for-løkke:
for /F "delims=;" %%I in ('dir /S /B /AD') do call :ProcessFolder "%%I" goto :eof :ProcessFolder for /F "delims=;" %%Z in ('dir /B "%~1\*.jpg"') do echo "%~1\%%Z"
Nu var det jo meningen at der skulle kaldes en funktion til at kopiere filerne i stedet for at udskrive navnet, det gøres simpelt ved at erstatte "echo" med "call :CopyFile" i sidste linie i koden ovenfor.
I funktionen CopyFile vil filenavnet inklusive stien være %1 så her skal du i første omgang lave en kommando der kopierer filen til "e:\kamera\%dato%".
Så kan du senere tilføje tæller og check til CopyFile-funktionen.
Husk: Du skal nok bruge en eller flere "goto :eof".
Sådan, så skulle den være der! Hvordan gør jeg: "Så kan du senere tilføje tæller og check til CopyFile-funktionen."?
Den log-fil vi er ved at lave, den kan jeg vel bruge i andre scripts? Her tænker jeg på, om jeg bare uden videre kan kopiere koden ind i et andet script.
set dato=%date:~3% if exist "e:\kamera\%dato%" ( echo Mappen findes ) else ( md "e:\kamera\%dato%" ) for /F "delims=;" %%I in ('dir /S /B /AD o:\') do call :ProcessFolder "%%I" goto :eof :ProcessFolder for /F "delims=;" %%Z in ('dir /B "%~1\*.jpg"') do call :CopyFile "%~1\%%Z" goto :eof :CopyFile xcopy/i/y %1 "e:\kamera\%dato%"
I starten af scriptet sætter du en tæller til at være 0 (set /A Counter=0) For hver fil der kopieres i :CopyFile tælles tælleren en op (set /A Counter+=1).
Man kan lave checks på mange måder, nogle mere grundige end andre. Man kunne starte med at finde ud af om filen blev kopieret vha: "if exist ...." Man kan også checke output fra kommandoen der kopierer, f.ex. checke det den skriver vha. find kommandoen, ca sådan:
xcopy/i/y %1 "e:\kamera\%dato%" |find "whatever" if errorlevel 1 echo Fail.
whatever skal erstattes med noget xcopy skriver når den fik kopieret en fil, og "echo Fail" skal erstattes med skrivning af en fejlmeddelse til logfilen.
Der er i øvrigt ingen grund til at bruge xcopy til at kopierer en enkelt fil, copy kan godt finde ud af det.
Jeg forstår ikke helt "whatever" (som jeg har sæt til %1).
Kan jeg bruge log-filen til en simpel xcopy kommando som f.eks. kunne kopiere ens dokumenter (som viser antal af filer som er kopieret, evt. fejl m.m.)? Eller kan vi lave et sådanne bagefter?
@echo off set /A Counter=0 set dato=%date:~3% if exist "e:\kamera\%dato%" ( echo Mappen findes ) else ( md "e:\kamera\%dato%" ) for /F "delims=;" %%I in ('dir /S /B /AD o:\') do call :ProcessFolder "%%I" goto :eof :ProcessFolder for /F "delims=;" %%Z in ('dir /B "%~1\*.jpg"') do call :CopyFile "%~1\%%Z" goto :eof :CopyFile set /A Counter+=1 xcopy/i/y %1 "e:\kamera\%dato%" |find "%1" if errorlevel 1 echo Fail.
Du kan godt putte output fra xcopy i log-filen direkte:
xcopy/i/y %1 "e:\kamera\%dato%" >logfil.txt
Jeg ville dog selv foretrække en log fil hvor der kun stod hvor mange filer der var kopieret og om der var nogen fejl.
Du kan evt. tilføje /F til xcopy, xå du kan se fuld path for destination og source.
Når jeg kopierer en fil med copy skriver den "1 file(s) copied." Hvis man sætter find til at lede efter dette og den ikke finder det, vil den sætte errorlevel. Så jeg kan bruge "1 file(s) copied." i stedet for "whatever" ovenfor. En dansk Win2000 vil sansynligvis skrive noget andet.
I :ProcessFolder kan du lave et check for om der findes .jpg billeder i mappen ved at bruge "if exist ....". Du kan blot springe mappen over (goto :eof) hvis der ikke findes nogen .jpg billeder.
Jo, vi kan godt fortsætte med log-filen, og ja, det ville nok være på plads med et nyt spørgsmål.
Hvis ikke du vil arbejde videre med opgave 2 og 3 vil jeg forsøge at lave en løsning til disse, så vi kan få dette spørgsmål afsluttet på en ordentlig måde.
Okay, så kalder jeg spørgsmålet "Bat programmering del 3".
Det har jo ikke så meget med scriptet fra "03/12-2003 00:39:27" at gøre. Det er mere noget med xcopy, og hvordan man tilføjer en log-fil (01/12-2003 22:52:41).
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.