Batch-Programmierung: Programmierungshilfen
Aus Wikibooks
[Bearbeiten] Ändern des Editors zum Bearbeiten von Batchdateien
Wenn man im Windows Explorer mit der Rechten-Maus-Taste (RMT) auf eine *.bat klickt, so werden einem die Befehle Öffnen und Bearbeiten angeboten.
Öffnen: führt die Batchdatei aus. Mit dem Befehl
Bearbeiten: wird die Batchdatei in den Texteditor notepad.exe zum Bearbeiten geöffnet.
Auch wenn Notpad zum Bearbeiten von Batchdateien ausreicht, so möchte man häufig doch die Batchdateien mit einem anderen, konfortableren Editor bearbeiten, der z.B. Syntaxhervorhebung (Syntaxhighlighting) beherrscht.
Um einen anderen Editor (z.b. Syn) zu verwenden, muss man in der Registry an der Stelle:
HKEY_CLASSES_ROOT\batfile\shell\edit\command
den Standard Wert
(Standard) = %SystemRoot%\System32\NOTEPAD.EXE %1
auf den Startbefehl des entsprechenden Editors ändern. Z.B.
HKEY_CLASSES_ROOT\batfile\shell\edit\command | (Standard) = c:\Programme\Editor\syn\syn.exe %1
[Bearbeiten] Datum und Uhrzeit anzeigen
Für Log-Dateien ist es wichtig, dass man die Logeinträge mit Datum und Uhrzeit versehen kann:
echo %date:~0% - %time:~0,8% Uhr
Ergebnis: 12.01.2007 - 15:59:53 Uhr
Hierbei steht ":~0,8" für die Angabe der Stellen. Mit "0,8" wird angegeben, dass die Ausgabe der Zeit bei Position 0 beginnen soll und insgesamt 8 Stellen beinhalten soll. Die maximale Stellenanzahl ist 11 (0,11).
Beim Datum ist diese Angabe hier nicht nötig, da dieses standardmäßig im dd.mm.yyyy-Format ausgegeben wird. Wer aber nur das Jahr haben will, kann "%date:~-4%" eingeben und erhält damit die letzten 4 Zeichen. Für ein sortiergerechtes Datum in der Umgebungsvariablen sortdate sorgt z.B.
set SORTDATE=%date:~-4%-%date:~3,2%-%date:~0,2% echo %SORTDATE%
Wert der Umgebungsvariable: 2009-04-20
Beachte:
In einer Batchdatei kann die Verwendung von %DATE% und insbesondere von %TIME% dazuführen, das die ausgegebene Uhrzeit sich nicht aktualisiert.
Hierzu folgendes Beispiel:
@echo off echo ## Die aktuelle Zeit ist: %TIME% echo ## bitte ca. 5 Sec. warten ... ping -n 5 localhost>NUL echo ## jetzt sollten ca. 5 Sec. vergangen sein, TIME liefert %TIME%, das ist noch OK echo ## doch in der FOR Schleife wird bereits die alte Zeit verwendet. for /L %%N IN (0, 1, 3) DO ( echo %time% pause ) echo ## und dies beleibt fuer jede Ausgabe innerhalb der FOR-Schleife so. echo. echo ## Auch in z.B. IF-Schleifen ist das so. if TRUE==TRUE ( echo 1. Zeit in der If Schleife: %TIME% echo Warte ca. 5 Sec. ping -n 5 localhost >NUL echo 2. Zeit in der If Schleife: %TIME% echo Warte nochmals ca. 5 Sec. ping -n 5 localhost >NUL echo 3. Zeit in der If Schleife: %TIME% ) echo ## Dabei ist es bereits: %TIME% pause
Damit %DATE% und %TIME% die richtigen Werte ausgeben, muss unbedingt die verzögerte Erweiterung von Umgebungsvariablen mit dem Befehl SETLOCAL ENABLEEXTENSIONS aktiviert werden.
Hier das korrekte Beispiel:
@echo off SetLocal EnableDelayedExpansion echo ## Die aktuelle Zeit ist: !time:~0,8! echo ## bitte ca. 5 Sec. warten ... ping -n 5 localhost>NUL echo ## jetzt sollten ca. 5 Sec. vergangen sein, TIME liefert !TIME!, das ist OK echo ## Jetzt gibt auch die FOR Schleife die korrekte Zeit aus. for /L %%N IN (0, 1, 3) DO ( echo !TIME! pause ) echo. echo ## Auch in z.B. IF-Schleifen ist es jetzt richtig. if TRUE==TRUE ( echo 1. Zeit in der If Schleife: !TIME! echo Warte ca. 5 Sec. ping -n 5 localhost >NUL echo 2. Zeit in der If Schleife: !TIME! echo Warte nochmals ca. 5 Sec. ping -n 5 localhost >NUL echo 3. Zeit in der If Schleife: !TIME! ) echo ## Es ist jetzt: !TIME! EndLocal pause
[Bearbeiten] Ausgaben besser anzeigen
Wenn man nicht die Ausgabe von Befehlen per @echo off "Ausblendet" kann man am besten das Prompt ändern, sodass man besser erkennen kann, was passiert:
@prompt -$G
Der Prompt ist dann ->
[Bearbeiten] Unterroutinen und Unterprogramme
Unterroutinen kann man mittels goto oder call und Unterprogramme mit Hilfe von call realisieren.
call:unterroutine Hallo
echo Fertig!
goto:eof
:unterroutine
echo Übergebener Parameter an Unterroutine: %1
goto:eof
Beachten Sie, dass beim Aufruf von Unterroutinen per call Sie Probleme mit Filehandles bekommen können. Dies liegt darin begründet, dass ein Aufruf per call als Aufruf eines Unterprogramms interpretiert wird, während es sich bei goto stets um Unterroutines handelt.
Anmerkung: goto:eof ist eine Spezialmarke mit der Sie stets zum Ende Ihres Skriptes (bzw. Ihrer Unterroutine) springen
[Bearbeiten] Benutzereingaben mittels "set /P"
@echo off
set /P w= [i]nstallieren / [d]eInstallieren?
REM die option /I beim if bewirkt, dass nicht
REM zwischen Gross und Kleinschreibung
REM unterschieden wird.
if /I "%w%"=="i" goto Install
if /I "%w%"=="d" goto Deinstall
echo Fehler: [%w%]
goto ende
:Install
echo "installieren" ausgewählt
goto ende
:Deinstall
echo "deInstallieren" ausgewählt
goto ende
:ende
echo.
pause
[Bearbeiten] stdout in Umgebungsvariable speichern
Falls man den stdout in einer Umgebungsvariable speichern möchte, muss man das komplizierter umsetzen. Es gibt zwei verschiedene Möglichkeiten dies anzugehen. befehl | set /P variable= funktioniert nämlich nicht. Stattdessen braucht man:
BEFEHL > temp.txt set /p BefehlOutput= < temp.txt del temp.txt
Oder:
FOR /F %%i IN ('BEFEHL') DO set BefehlOutput=%%i
Oder mit "usebackq"-Option:
FOR /F "usebackq" %%i IN (`BEFEHL`) DO set BefehlOutput=%%i
Die Zeichenkette zwischen den einfachen Anführungszeichen wird dabei als Befehlszeile betrachtet und von einer untergeordneten CMD.EXE ausgeführt. %BefehlOutput% kann nun beliebig gebraucht werden.
[Bearbeiten] Beispiel:
Code:
@echo off
FOR /F %%i IN ('CD') DO set verzeichnis=%%i
echo %verzeichnis%
Ausgabe:
C:\Programme\Batch
Vorsicht ist geboten, bei Befehlen, welche mehrzeilige Ausgaben produzieren und bei solchen, welche in ihrer Ausgabe auch Leerzeichen enthalten können. Da das Standardtrennzeichen ein Blank ist muss man, wenn man nicht will, dass die Variable nur bis zum Blank gefüllt wird, das Standardtrennzeichen verändern. FOR /F "delims=" %%i IN ('CD') DO set verzeichnis=%%i entfernt jede Art von Trennzeichen. Bei Befehlen, welche mehrzeilige Ausgaben zur Folge haben, bleibt jeweils die letzte Zeile in der Variablen erhalten.
[Bearbeiten] Dateien und Verzeichnisse auflisten
Hier ist ein Beispiel, in dem alle Dateien, auf welche die Filterbedingung zutrifft, aufgelistet werden. Ausserdem werden die Dateianzahl und die Dateigrößen zusammenaddiert.
@echo off
set Filter=*.*
set /A DateiAnzahl=0
set bytes=0
for /R %pfad% %%f in (%Filter%) do (
set /A DateiAnzahl += 1
echo %%f - %%~zfBytes
set /A bytes=bytes+%%~zf
)
echo.
echo %~dp0%Filter%
echo Es sind %DateiAnzahl% Dateien vorhanden.
echo Alle Dateien zusammen: %bytes%Bytes
set /A kbytes=bytes/1024
echo umgerechnet sind das %kbytes% KBytes
echo.
pause
[Bearbeiten] Pause
Oft ist es hilfreich, dass ein nach dem Beenden des Batch-Programms das Eingabeaufforderungsfenster offen bleibt. So kann man Ausgaben nachlesen oder evtl. aufgetretene Fehler entdecken. Nun könnte man einfach am Ende eine pause einfügen. Dabei kann der User einfach das Fenster schließen oder ENTER drücken. Man kann aber auch einfach eine zeitliche Pause mit ping realisieren:
@echo off echo Ich schließe gleich. @ping localhost -n 2 >NUL
Dabei kann man die Zeit mit dem Parameter -n variieren.
Bei installiertem Resourcekit steht der Befehl "sleep" zur Verfügung welcher dieselbe Funktionalität (zeitliche Pause) bietet.
[Bearbeiten] Minimiert ausführen
Hin und wieder ist es sinnvoll, dass die Batchdatei minimiert ausgeführt wird (z. B. eine Login-Batch-Datei). Es ist möglich, dass man die Batchdatei normal startet und sie sich selber minimiert ausführt. Der Nachteil ist allerdings, dass sich kurzzeitig ein Eingabeaufforderungs-Fenster öffnet.
@echo off
if not "%1"=="" goto %1
start /MIN cmd.exe /C "%~nx0 begin"
goto:eof
:begin
echo Hallo, ich laufe minimiert!
pause
goto:eof
Noch eine Konstruktion ganz ohne Labels, nach diesem Newsgroup-Beitrag
@set !=||(set !=1&start "%~dpnx0" /min cmd /c %0 %*&set !=&goto :eof)
[Anmerkung zum Newsgroup-Beitrag] Sollte die Command-Processor-Option "DelayedExpansion" in der Registry aktiviert sein (siehe unter Hilfe "cmd /?"), lässt sich ein "!" als Variablenname nicht verwenden. In diesem Fall -bzw. sinnvollerweise immer- den Variablennamen ändern auf x oder y oder # oder @....
Beispiel: @set #=||(set #=1&start "%~dpnx0" /min cmd /c %0 %*&set #=&goto :eof)
[Bearbeiten] Mittels start /LOW die Priorität festlegen
Manchmal ist es hilfreich, wenn die Batchdatei mit einer niedrigen Priorität läuft. Das kann man mittels start /LOW erreichen. Weitere Optionen sind NORMAL, HIGH, REALTIME, ABOVENORMAL und BELOWNORMAL. Das Beispiel zeigt, wie eine Batchdatei quasi sich selber in die niedrige Priorität versetzten kann. In dem Fall klappt es allerdings nur, wenn beim ersten Start kein Parameter übergeben wurde.
@echo off
if "%1"=="" (
start /WAIT /LOW /B cmd.exe /V /C %~s0 weiter_machen
goto:eof
)
echo Jetzt laufe ich mit niedriger Priorität!
echo Überprüfe es im Taskmanager!
pause
Funktionsweise: Das Prinzip ist eigentlich ganz einfach. Wenn kein Parameter übergeben wird, wird angenommen, das die Batchdatei zum ersten mal gestartet wurde. Die if "%1"=="" Bedingung ist also erfüllt. Mittels start wird dann die selbe Batchdatei mit veränderter Priorität gestartet, allerdings mit einem angehängten Parameter weiter_machen (Könnte auch irgendwas anderes sein!) Somit ist beim nächsten Aufruf die if "%1"=="" Bedingung nicht mehr erfüllt und der normale Teil der Batchdatei wird abgearbeitet.
Wenn man der Batchdatei einen Parameter übergeben möchte (z.B. ein Dateiname o.ä.) muß man alle Parameter verschieben:
@echo off
if "%2"=="" (
start /WAIT /LOW /B cmd.exe /V /C %~s0 %1 weiter_machen
goto:eof
)
echo Jetzt laufe ich mit niedriger Priorität!
echo Nun kann [%1] 'bearbeitet' werden...
pause
Anmerkungen: Normalerweise könnte man statt %~s0 auch "%~0" bei der cmd.exe Zeile verwenden. Allerdings klappt das nicht richtig, wenn Leerzeichen in der Batch Datei vorhanden sind. Mit %~s0 wird der komplette Pfad zur Batchdatei als "Kurznamen" angegeben. In dem Pfad kommt dann keine Leerzeichen vor.
[Bearbeiten] Probleme mit Variablen
Wenn man sich die Hilfeseiten zu set (mittels set /?) durchliest, stößt man auf das Thema verzögerte Erweiterung von Variablen. Das will ich hier mal anhand von Beispielen erklären:
[Bearbeiten] Das Problem
set test=1
if "%test%"=="1" (
set test=2
echo Wert von 'test' im IF-Block: %test%
)
echo Wert von 'test' nach IF-Block: %test%
Man sollte meinen, dass der Wert von %test% in beiden Ausgaben 2 ist. Doch leider ist es nicht so. Denn innerhalb des IF-Blocks wird das Neusetzen der Variable test von 1 auf 2 noch nicht aktiv und somit ist das Ergebnis Wert von 'test' im IF-Block: 1 Erst nach dem IF-Block ist der Wert aktualisiert: Wert von 'test' nach IF-Block: 2
[Bearbeiten] Lösung: cmd.exe /V
In einer Batchdatei die mit cmd /V gestartet wurde, werden Variablen innerhalb von Befehlsblöcken aktualisiert. Jedoch kann man sie nicht gewohnt mit %test% ansprechen, sondern mit !test!
@echo off
if "%1"=="" (
start /WAIT /B cmd.exe /V /C "%~0" machen!
goto:eof
)
set test=1
if "%test%"=="1" (
set test=2
echo Wert von 'test'-Prozent in dem IF-Block: %test%
echo Wert von 'test'-Ausrufezeichen in dem IF-Block: !test!
)
echo Wert von 'test' nach dem IF-Block: %test%
pause
[Bearbeiten] Ausgaben/Fehler unterdrücken
Manchmal möchte man per Batch ein Programm starten aber es sollen dabei keine Ausgabe gemacht werden. Das ist recht einfach:
MeinProgramm.exe >NUL
Es könnte aber sein, dass evtl. Fehler dennoch ausgegeben werden. Das liegt daran, das die Programme in dem Fall auf stderr statt stdout schreiben. Um auch in dem Fall die Ausgabe zu unterdrücken, kann man mit einem zusätzlichen 2>&1 die Ausgaben von stderr auf stdout umleiten. Da stdout dann nach NUL verschoben wird, sieht man absolut nichts:
MeinProgramm.exe >NUL 2>&1
[Bearbeiten] Professionelle Message-Fenster erzeugen
Bisher konnte man in Batch keine Fenster erzeugen, bzw. nur in Windows XP mithilfe des Windows Nachrichtendienstes. Doch der ist für Anwendungen viel zu unpraktisch, da man nicht einmal den Fenstertitel bestimmen kann und außerdem ist dieser unter Vista oder Windows 2000 nicht verfügbar.
Ich habe jedoch ein kleines Schlupfloch gefunden, mit dem man dennoch Fenster erzeugen kann:
@echo off Echo msgbox"Text",0,"Fenstername" >Test.vbs ping localhost -n 3 -w 1000 >NUL start Test.vbs pause
Erklärung: Man erzeugt hier mithilfe des Operators > einen Temporären VBScript, der in der Lage ist, ein Messagefenster zu erzeugen. Dies sorgt in Anwendungen für mehr Übersicht und Professionalität.
[Bearbeiten] Falls das nicht funktioniert
Falls statt eures Textes die Meldung :
"Der Zugriff auf den Windows Scripthost ist auf diesem Computer deaktiviert"... erscheint, dann ist das Öffnen von VBscripts und JScripts aus Sicherheitsgründen verboten.
Um das zu ändern, öffnet ihr den Windows Registrierungseditor (regedit.exe) und löscht NUR folgenden Wert :
HKey_Local_Machine\Software\Microsoft\WindowsScriptHost\Enabled
dann dürfte es funktionieren. Erstellt vor der Änderung eine Sicherheitskopie eurer Werte !!!