evotoy
Goto Top

Batch - mögliche Codeoptimierung?

Hi,

ich habe eine kleine Batch geschrieben, die rudimentäre Sortieraufgaben übernimmt. Meine BATCH-Erfahrungen sind leider noch überschaubar.

FRAGE: ist es möglich den folgenden Code so zu optimieren, dass die Batch schneller arbeitet?

@echo off

set "MaskSort=*.jpg  
for /F "delims=" %%i in ('dir/b %MaskSort%') do set "Name=%%~ni" & call :ProcessFile "%%i"  
echo Sortiervorgang beendet.   
goto :eof

:ProcessFile

if exist "%Name:~,-4%" (  
        move "%name%%~x1" "%Name:~,-4%\" 2>NUL  
    ) ELSE (
        echo Erstelle Ordner   : %Name:~,-4%   
        md "%Name:~,-4%" 2>NUL & move "%name%%~x1" "%Name:~,-4%\" 2>NUL  
    )
)

mir is aufgefallen, dass seit ich mit Sprungmarken arbeite die Scripe langsamer laufen als ohne.

Mit Tokens und Delims komm ich der Sache leider nicht bei. Wenn ich die Sprungmarke rausnehme habe ich das Problem, dass in die Variablen in der Schleife nicht gehen. Die Variable brauch ich aber für die Sringmanupulation.
Ich sortiere mit dem Script ca. 10.000 Files - das dauert leider ewig. Der Code hier hat zB. wesentlich schneller gearbeitet:

for /F "tokens=2 delims=-" %%i in ('dir/b') do (   
	if exist "%%~ni" (  
		move "*%%~ni.*" "%%~ni\" 2>NUL  
	) ELSE (
		echo Erstelle Ordner   : %%~dpni   
		md "%%~ni" 2>NUL  
		move "*%%~ni.*" "%%~ni\" 2>NUL  
	)
)

Gibt es eine Möglichkeit den Code von oben so zu optimieren, dass das Script schneller arbeitet? Oder muss man dann direkt auf VBS oder ähnliches umsteigen?

Danke und vg,

Content-ID: 160957

Url: https://administrator.de/contentid/160957

Ausgedruckt am: 25.11.2024 um 00:11 Uhr

Skyemugen
Skyemugen 16.02.2011 um 18:46:27 Uhr
Goto Top
Aloha,

da der Thread schonmal Codeoptimierung heißt, würde ich vorallem die Formatierung im Forum optimieren:
echo mit den passenden tags >>nächste_Zeile


=)

greetz André
evotoy
evotoy 16.02.2011 um 19:07:43 Uhr
Goto Top
Besser???

ich hab leider zu selten Zeit zum batchen und ich bindeshalb auch zu selten hier face-sad
bastla
bastla 16.02.2011 um 19:10:12 Uhr
Goto Top
Hallo evotoy!

Soferne es keine Namen mit enthaltenen "!" gibt, könntest Du es so versuchen (ungetestet):
@echo off & setlocal enabledelayedexpansion

set "MaskSort=*.jpg  
for /F "delims=" %%i in ('dir/b /a-d %MaskSort%') do (  
    set "Name=%%~ni"  
    set "Name=!Name:~,-4!"  
    md "!Name!" 2>nul  
    move "%%i" "!Name!\"  
)
echo Sortiervorgang beendet.
So wird auch nur eine Stringzerlegung (und keine Abfrage face-wink) benötigt.

Grüße
bastla
pieh-ejdsch
pieh-ejdsch 16.02.2011 um 19:30:58 Uhr
Goto Top
moin evotoy,

mir is aufgefallen, dass seit ich mit Sprungmarken arbeite die Scripe langsamer laufen als ohne.
...
Gibt es eine Möglichkeit den Code von oben so zu optimieren, dass das Script schneller arbeitet? Oder muss man dann direkt auf VBS oder ähnliches umsteigen?

schneller als das, was bastla als lösung gepostet hat wirst Du es nicht mehr mit batch bekommen. Das mag daran liegen, das eine Variable (2mal) erst gesetzt wird und auch wieder aufgelöst wird. Das kostet Zeit.
was Du noch überprüfen kannst:
Bei Batches mit Sprungmarken bzw. generell Batches - Ist im Online-Virenscanner die Batch in den Ausnahmen deklariert.

Gruß Phil
Biber
Biber 16.02.2011 um 19:50:08 Uhr
Goto Top
Na ja, pieh-ejdsch,

diese Antwort ist mir zu technisch...
Eine "Optimierung eines Ablaufs" ist doch nicht nur "benutze ich eine oder zwei Stringzerlegungen". Oder ein goto oder verzögerte Variablen.

Bei den oben beschriebenen 10000 zu verteilenden *.jpgs kommt es sicher eher auf die (vorhersagbare) Verteilung in einzelne Ordner an.

Wenn beispielsweise der obige Batch läuft und 10000 Dateien werden in 10000 Ordner verschoben von denen 9999 neu angelegt werden und einer vorher existiert...
Dann ist es die "passende" Lösung.

Wenn allerdings die 10000 Dateien in nur drei Ordner verschoben/verteilt werden sollen, die auch alle vorher existieren...
-> dann würde sicherlich ärgerlich viel Zeit damit verbraten, einfach nur das überflüssige "md "!Name!" 2>nul" abzuwarten.

In dem unterem Szenario wäre es eleganter, nicht die Dateien einzeln abzuklappern, sondern die bestehenden Zielordner.
Und die Dateien auch mit Wildcards en bloc zu verschieben.

Oder drei Parallel-Threads für die drei Ordner loszujagen...


Aber da kann man/frau nur ansetzen, wenn denn dieses Sortierkrams a) regelmäßig und b) mit vorhersagbarer Ausgangssituation stattfindet.

Grüße
Biber
bastla
bastla 16.02.2011 um 19:52:42 Uhr
Goto Top
@ph
Nach einem kurzen Test (mit nur einer Zählschleife mit 30000 Durchläufen und einem "echo" der beiden Befehle) könnte es tatsächlich effizienter sein, nur eine einmalige Variablenzuweisung und dafür die zweimalige Teilstringbildung zu verwenden - als Schleifeninhalt daher:
    set "Name=%%~ni"  
    md "!Name:~,-4!" 2>nul  
    move "%%i" "!Name:~,-4!\"  
Der Unterschied lag bei etwa 5 % ...

Grüße
bastla

[Edit] @Biber
"Optimierung eines Ablaufs"
wäre dann vermutlich das Thema des nächsten Threads geworden ... face-wink
[/Edit]
pieh-ejdsch
pieh-ejdsch 16.02.2011 um 20:27:16 Uhr
Goto Top
diese Antwort ist mir zu technisch...
wohl eher auch noch zu Allgemein gehalten.
In dem unterem Szenario wäre es eleganter, nicht die Dateien einzeln abzuklappern, sondern die bestehenenden Zielordner.
Und die Dateien auch mit Wildcards en bloc zu verschieben.
das wäre dann die Option für den ersten Teil des Scriptes, damit im zweiten Teil nicht soviel ["md Ordner" aber ohne Fehlermeldung] durchgeführt werden.

@bastla hast Du Die Ausgabe Weggeleitet?

da das Auflösen einer Variable etwas länger dauert als das setzen - dürfte, wenn ich mit meiner Theorie nicht falsch liege, die mindest-zweimalige Auflösung dieser Variable als Laufvariable noch einen Zacken schneller sein als das Direkte Auflösen der Variable.

ist ein "if not exist Ordner md Ordner" nicht schneller als ein "md Ordner 2>nul"

Gruß Phil
jeb-the-batcher
jeb-the-batcher 16.02.2011 um 21:16:42 Uhr
Goto Top
Zitat von @bastla:
Hallo evotoy!

Soferne es keine Namen mit enthaltenen "!" gibt, könntest Du es so versuchen (ungetestet):

Damit es auch mit dem bösen "!" klappt, kann man einfach den Block noch ein bisschen mit Setlocal und Endlocal würzen.

@echo off
setlocal DisableDelayedExpansion

set "MaskSort=*.jpg  
for /F "tokens=* delims=" %%i in ('dir/b /a-d %MaskSort%') do (  
    set "Name=%%~ni"  
    set "OrgName=%%~ni"  
    setlocal EnableDelayedExpansion
    set "Name=!Name:~,-4!"  
    md "!Name!"   
    move "!OrgName!" "!Name!\"  
    EndLocal
) 2>nul
echo Sortiervorgang beendet.

Und die Kombination "Tokens=* delims=" entspricht von der Ausgabe "delims=" ist aber schneller (abhängig von den Stringlängen).

Gruß
jeb
bastla
bastla 16.02.2011 um 21:21:50 Uhr
Goto Top
@jeb
Damit es auch mit dem bösen "!" klappt, kann man einfach den Block noch ein bisschen mit Setlocal und Endlocal würzen.
Das ist schon klar, aber nicht Im Sinne der Optimierung (wenn eben "!" auszuschließen sind) ...

Grüße
bastla
pieh-ejdsch
pieh-ejdsch 16.02.2011 um 21:39:43 Uhr
Goto Top
moin jeb,

Und die Kombination "Tokens=* delims=" entspricht von der Ausgabe "delims=" ist aber schneller (abhängig von den Stringlängen).
wie meinst Du abhängig?
bei beiden oder nur bei "delims=" ?
versucht die Forschleife etwa erst den ganzen Sting in viele Tokens (welche ja nur eines sind) zu zerlegen und dann werden diese wieder zusammengesetzt?
sonst müsste es ja keinen messbaren Unterschied geben.

Gruß Phil
Biber
Biber 16.02.2011 um 22:14:38 Uhr
Goto Top
Wenn wir hier ohnehin alle durcheinander rumirrlichtern, ohne den TO abzuwarten...
... einen habe ich auch noch...

for /l %%a in (1,1,30000) do if not exist R:\bla md R:\bla 1003 ms
for /l %%a in (1,1,30000) do md R:\bla 2>nul 4445 ms

Wozu denn jetzt blind irgendwelche Perlen/Ausrufezeichen/in Tränen aufgelösten Variablen in die Nacht/vor die Säue/durchs Dorf werfen/treiben?

Menno, früher haben wir die Menschen darauf hingewiesen, dass Bätche sogar unbeaufsichtigt laufen können, wenn sie sauber geschrotet sind.

Who cares denn, ob das Verschieben von 10000 Dateien 10 Minuten oder 45 Minuten dauert?
Wenn ich den Windows-Explorer damit losjage und der mir seine fliegenden Datenklumpen hinmalt, dann dauert es prognostiziert zwischen 45 und 7865 Minuten und er ist je nach Laune zu 3% ich korrigiere 44% ich korrigiere 9% ich korrigiere 144% fertig...

5 % Laufzeitoptimierung bei einem Batch.... dafür steh ich nicht nachts auf...

Grüße
Biber
jeb-the-batcher
jeb-the-batcher 16.02.2011 um 23:41:43 Uhr
Goto Top
Zitat von @Biber:
Wenn ich den Windows-Explorer damit losjage und der mir seine fliegenden Datenklumpen hinmalt, dann dauert es prognostiziert
zwischen 45 und 7865 Minuten und er ist je nach Laune zu 3% ich korrigiere 44% ich korrigiere 9% ich korrigiere 144% fertig...

5 % Laufzeitoptimierung bei einem Batch.... dafür steh ich nicht nachts auf...

Wie recht er hat!

tokens=* ist schneller als delims=, macht aber nunmal auch was anderes, schluckt so nebenbei am Anfang alle Spaces und Tabs.
Zusammen sind sie stark und schnell.
Allerdings nur wenn die Daten auch lang sind, bei kurzen Strings oder hier dem Dateiname, geht der Unterschied so ziemlich gegen 0,0%.

Bei langen Strings wird es dann aber immer größer, so emotional gefühlte 50%-200%. (Ab 1000Zeichen ++).
Vermutlich hier nicht ganz so relevant, ausser die Dateinamen wären überraschend lang.

Das Verschieben der Dateien dürfte allerdings bereits beendet sein, während hier noch über die schnellste Lösung debattiert wird.

Grüße
jeb
evotoy
evotoy 17.02.2011 um 00:10:23 Uhr
Goto Top
wow ... danke für die vielen Posts ... bis ich da durch bin brauch ich aber n bisschen face-smile ... bin schon gespannt was da noch so geht! Probier das morgen gleich mal aus ...

bis denne
evotoy
evotoy 17.02.2011 um 00:56:26 Uhr
Goto Top
ich nochmal ... hab jetzt alle Posts durch - möchte aber nicht behaupten, dass ich da komplett mitkomme. Ich hab mir doch nochmal den Spaß gemacht die Files im Explorer auzulesen - das mach ich nie wieder!! muss die Zahl etwas noch oben korrigieren. Sind nicht 10.000 sondern 100.000+ Files (nach dem Sortieren ca 50 Ordner mit je 2000Files) ????? Da sollte ich mir generell was anderes einfallen lassen ;) aber ich will ja meine basic Batch skills verbessern und es wurmt mich halt selber tierisch, dass ichs nicht schneller hinbekomme! ... auf jeden Fall macht das schon nen krassen Unterschied ob man 40 Min vorm Rechner sitzt und wartet oder 5 ... erst recht wenn der Feierabend entsprechend nach hinten rückt ... 5% sind bei 5 Min dann nicht mehr so relevant aber die 40 sind definitiv nicht so cool und das ist der aktuelle Stand.

Ich teste das auf jeden Fall mal das Script von Bastla und dann versuch ich mal das von jtbatcher zu schnallen. Das Script von Bastla hatte ich heut schonmal fast so - allerdings ohne die "!" - ging natürlich nicht - was muss man denn da googeln wenn man zu !Name! was finden will? Ich hab dazu schonmal was gelesen - hab aber keine Idee nach was ich da eigentlich suchen soll (?????Variable). Das Script läuft damit aber bestimmt schonmal ne ganze Ecke schneller als meins und die Sequenzen als Blöcke zu verschieben könnte auch nochmal extremst was bringen. Damit könnte man 2000 Files mit einem Durchlauf abfrühstücken ...

bis nach dem Test

grüße
bastla
bastla 17.02.2011 um 08:44:40 Uhr
Goto Top
Hallo evotoy!

Vermutlich wird's dann die Kombination bringen - etwa:
@echo off & setlocal
set "MaskSort=*.jpg"  
for /d %%i in (*) do move "%%i%MaskSort%" "%%i"  
for /F "tokens=* delims=" %%i in ('dir/b /a-d %MaskSort%') do (  
    set "Name=%%~ni"  
    set "OrgName=%%~ni"  
    setlocal EnableDelayedExpansion
    set "Name=!Name:~,-4!"  
    if not exist "!Name!" md "!Name!"   
    move "!OrgName!" "!Name!\"  
    endlocal
)
echo Sortiervorgang beendet.
[Edit] Überzähligen "*" aus "move"-Quelle entfernt [/Edit]
Dein gesuchtes Suchwort ist übrigens "delayedExpansion" ...

Grüße
bastla
Biber
Biber 17.02.2011 um 08:49:51 Uhr
Goto Top
@bastla
Muss es in Zeile 3 nicht statt "...move "%%i*%MaskSort%" besser lauten "...move "%%i\%MaskSort%" ?
Nein, Unsinn...
Wenn schon, dann
 ...
Set "SortMask=.jpg"  
for /d %%i in (*) do @echo move "*%%i%Sortmask%" "%%i\"  
...
Beispiel:
>for /d %i in (*) do @echo move "*%i.jpg" "%i\"
move "*logs.jpg" "logs\"

Grüße
Biber
bastla
bastla 17.02.2011 um 08:57:35 Uhr
Goto Top
@Biber
Da aus dem aktuellen Ordner in dessen entsprechende Unterordner verschoben werden soll, sollte das so schon passen - Beispiel: In den Unterordner "Bilder105" sollten alle "Bilder105????.jpg" verschoben werden (wobei die Schreibweise mit "*" zugegeben etwas weniger genau ist) ...

[Edit] Da "*" ja schon in %Maskort% enthalten ist, ginge es ja auch gar nicht mit "????" - allerdings kann er dann auch in der Quellangabe eingespart werden [/Edit]

Grüße
bastla
pieh-ejdsch
pieh-ejdsch 17.02.2011 um 09:02:02 Uhr
Goto Top
moin,

noch ne andere Strategie mit Liste (ungetestet)
::----snipp----MoveTo0,-4Folder.cmd
@echo Off
set Woher="D:\ein Verzeichnis"  
set "MaskSort=*.jpg"  
pushD %Woher%
if exist "%temp%\Dirtmp2" del  "%temp%\Dirtmp2"  
:Anfang
if exist "%temp%\Dirtmp" del "%temp%\Dirtmp"  
if exist "%temp%\Dirtmp2" (move "%temp%\Dirtmp2" "%temp%\Dirtmp">nul ) else dir /b %MaskSort%>"%temp%\Dirtmp"||(popD&goto :eof)  
set /p Name=<"%temp%\Dirtmp"  
if not exist "%Name:~,-4%" md "%Name:~,-4%"  
move "%Name:~,-4%????%MaskSort:~-4%" "Name:~,-4%"  
findstr /i /v /b "%Name:~,-4%....%Masksort:~-4%" "%temp%\Dirtmp">"%temp%\Dirtmp2"  
goto :Anfang
::----snapp----MoveTo0,-4Folder.cmd

[Edit] beim DEL "2>nul" entfernt und stattdessen if exist eingefügt
findstr Suchzeichenfolge nochmal angepasst
beim MOVE 4 Platzhalter eingesetzt statt ein Stern (sonst gehts in die Hose bei mehreren Namenlängen und gleichen anfang
[/Edit]


Gruß Phil
Biber
Biber 17.02.2011 um 09:06:12 Uhr
Goto Top
Sorry. bastla,

ich nehme alles zurück...
Ich habe die Namens-Abschneidekonvention falsch gelesen - es bleibt ja der Namens-Anfang erhalten.
Ich sollte niemals vor dem Morgenkaffee posten...

Grüße
Biber
evotoy
evotoy 19.02.2011 um 22:59:04 Uhr
Goto Top
Hi,

sorry dass ich mich die letzten Tage nicht melden konnte ... ich hab die Scripte alle durchgetestet. Musste allerdings feststellen, dass wirkliche Laufzeiten zu messen kaum machbar ist. Bei den Datenmengen kommt selbst der Explorer permanent ins Straucheln - selbst das Umbenennen eines Ordners dauert ca 7 Sekunden. Die Scripte funktionieren alle super!! Ich hab auf jeden Fall wieder was dazu gelernt ;) Schlussendlich hab ich jetzt den Source verwendet:

@echo off & setlocal enabledelayedexpansion
set "vorher=%time%"  

set "MaskSort=*.tif  
for /F "tokens=* delims=" %%i in ('dir/b /a-d %MaskSort%') do (  
    set "Name=%%~ni"  
    setlocal EnableDelayedExpansion
    set "Name=!Name:~,-4!"  
	if not exist "!Name!" md "!Name!"  
    move /y "!Name!*.*" "!Name!\"  
    EndLocal
) 2>nul
echo Sortiervorgang beendet.
set "danach=%time%"  
echo Laufzeit = %vorher% - %danach%
pause

Beim ersten Testdurchlauf war das Script von den fünf Varianten das Schnellste - aber die Aussage ist nur bedingt aussagekräftig. Beim zweiten Durchlauf kamen ganz andere Ergebnisse raus und die Scripte haben auch ab und an mal "gehangen" - schätze, dass meine lokalen Festplatten da gar nicht nicht mitkommen -120000 Files a 20kb-1,5MB - da sind die Köppe ganz schön unterwegs.

Also um da ne wirkliche Aussage zu treffen bräucht ich wohl n neu aufgesetztes RAID ...

Die Variante mit der Liste werde ich mir bei Gelegenheit auch noch einmal zu Gemüte führen - Vielen Dank für die Hilfe!
bastla
bastla 19.02.2011 um 23:57:35 Uhr
Goto Top
Hallo evotoy!

Letzter Optimierungsvorschlag: In einem Batch braucht "move" kein "/y" ... face-wink

Grüße
bastla