Top-Themen

AppleEntwicklungHardwareInternetLinuxMicrosoftMultimediaNetzwerkeOff TopicSicherheitSonstige SystemeVirtualisierungWeiterbildungZusammenarbeit

Aktuelle Themen

Administrator.de FeedbackApache ServerAppleAssemblerAudioAusbildungAuslandBackupBasicBatch & ShellBenchmarksBibliotheken & ToolkitsBlogsCloud-DiensteClusterCMSCPU, RAM, MainboardsCSSC und C++DatenbankenDatenschutzDebianDigitiales FernsehenDNSDrucker und ScannerDSL, VDSLE-BooksE-BusinessE-MailEntwicklungErkennung und -AbwehrExchange ServerFestplatten, SSD, RaidFirewallFlatratesGoogle AndroidGrafikGrafikkarten & MonitoreGroupwareHardwareHosting & HousingHTMLHumor (lol)Hyper-VIconsIDE & EditorenInformationsdiensteInstallationInstant MessagingInternetInternet DomäneniOSISDN & AnaloganschlüsseiTunesJavaJavaScriptKiXtartKVMLAN, WAN, WirelessLinuxLinux DesktopLinux NetzwerkLinux ToolsLinux UserverwaltungLizenzierungMac OS XMicrosoftMicrosoft OfficeMikroTik RouterOSMonitoringMultimediaMultimedia & ZubehörNetzwerkeNetzwerkgrundlagenNetzwerkmanagementNetzwerkprotokolleNotebook & ZubehörNovell NetwareOff TopicOpenOffice, LibreOfficeOutlook & MailPapierkorbPascal und DelphiPeripheriegerätePerlPHPPythonRechtliche FragenRedHat, CentOS, FedoraRouter & RoutingSambaSAN, NAS, DASSchriftartenSchulung & TrainingSEOServerServer-HardwareSicherheitSicherheits-ToolsSicherheitsgrundlagenSolarisSonstige SystemeSoziale NetzwerkeSpeicherkartenStudentenjobs & PraktikumSuche ProjektpartnerSuseSwitche und HubsTipps & TricksTK-Netze & GeräteUbuntuUMTS, EDGE & GPRSUtilitiesVB for ApplicationsVerschlüsselung & ZertifikateVideo & StreamingViren und TrojanerVirtualisierungVisual StudioVmwareVoice over IPWebbrowserWebentwicklungWeiterbildungWindows 7Windows 8Windows 10Windows InstallationWindows MobileWindows NetzwerkWindows ServerWindows SystemdateienWindows ToolsWindows UpdateWindows UserverwaltungWindows VistaWindows XPXenserverXMLZusammenarbeit

Batch - Flags im Exit-Code (Errorlevel) - per Bit-Operation mehrere Informationen in den Rückgabewert packen

Anleitung Entwicklung Batch & Shell

Mitglied: NeonZero

NeonZero (Level 1) - Jetzt verbinden

17.09.2008, aktualisiert 02.03.2011, 34936 Aufrufe, 1 Kommentar

Der Beitrag, inkl. Quelltext, ist Gemeinfrei (er darf ohne jegliche Einschränkung genutzt werden).
01.
Tipp: Wird der Inhalt in den Quelltextfenstern chaotisch angezeigt, kann es hilfreich sein dort auf den Link "Quelltext" (oben rechts) zu klicken. Es öffnet sich dann ein separates Fenster, welches in der maximierten Darstellung, je nach Bildschirmauflösung, den unerwünschten Zeilenumbruch unterbindet.

Sobald ein Konsoleprogramm beendet wird, kann es seinem Aufrufer einen numerischen Wert zurückgeben. Gleiches gilt für eine Batch-Datei und für eine Batch-Funktion.

Im Folgenden sollen Möglichkeiten aufgezeigt werden, um mehrere Zustände (im Folgenden Flags genannt) in diesen numerischen Rückgabewert zu packen. Der so aufbereitete Rückgabewert (engl. Returncode, auch Exitcode, Errorcode oder Errorlevel) soll es dem Aufrufer ermöglichen, die Zustände einzeln und in beliebiger Kombination zueinander abfragen zu können.


1. Grundlagen zum Exit-Code

Beispiel, um einen Rückgabewert an seinen Aufrufer zu übermitteln:
01.
Inhalt von MyBatch.cmd: 
02.
 @exit /b 5 ::Die Option /b sorgt dafür, dass hierbei nicht die aufrufende Konsole geschlossen wird 
03.
------------------------------ 
04.
Aufruf der Batch in der DOS-Konsole: 
05.
 c:\>MyBatch.cmd 
06.
 c:\>echo %errorlevel% 
07.
------------------------------ 
08.
Ausgabe: 5

Den Rückgabewert an eine Windowsanwendung übergeben

Wird die Batch aus einer Windowsanwendung heraus aufgerufen, so öffnet die dazugehörige API eine DOS-Konsole, die wiederum die Batch startet. Die Anwendung erhält danach den Rückgabewert der Konsole und nicht die der Batch.

Der Rückgabewert lässt sich durchschleifen, indem die Batch über exit <Zahl> (also ohne die Option /b) beendet wird. Das hat jedoch den Nachteil, dass ein Aufruf der Batch von Hand sogleich auch die Konsole beendet. Eine Lösung könnte der folgende Wrapper sein, den die Windowsanwendung nutzen kann, um eine Batch zu starten:
01.
Inhalt von Wrapper.cmd: 
02.
 @call %* 
03.
 @exit %errorlevel% 
04.
------------------------------ 
05.
Aufruf:  Wrapper.cmd  MyBatch.cmd <Parameter1> <Parameter2> ...

Den Rückgabewert bei der Nutzung einer separaten cmd (auch über start) an den Aufrufer übergeben

Unter Nutzung der oben gezeigten Wrapper.cmd lässt sich auch hier der Rückgabewert durchschleifen:
01.
Aufruf aus der DOS-Konsole heraus: 
02.
 c:\>MyBatch.cmd 
03.
 c:\>echo %errorlevel% 
04.
  Ausgabe: 5 
05.
 
06.
 c:\>cmd /c MyBatch.cmd 
07.
 c:\>echo %errorlevel% 
08.
  Ausgabe: 0 
09.
 c:\>cmd /c Wrappper.cmd MyBatch.cmd 
10.
 c:\>echo %errorlevel% 
11.
  Ausgabe: 5 
12.
 
13.
 c:\>start /wait "" MyBatch.cmd 
14.
 c:\>echo %errorlevel% 
15.
  Ausgabe: -1073741510 
16.
 c:\>start /wait "" Wrapper.cmd MyBatch.cmd 
17.
 c:\>echo %errorlevel% 
18.
  Ausgabe: 5

2. Ein Rückgabewert, der mehrere Zustände (Flags) verwaltet

Der Einfachheit halber soll ein Rückgabewert erzeugt werden, der angiebt , ob Datei1 gefunden wurde. Die Herausforderung besteht darin, dass derselbe Rückgabewert gleichzeitig auch eine entsprechende Aussage für Datei2 und Datei3 enthalten soll.

Lösung 1: Addition dezimaler Zahlen 1, 2, 4, 8, 16, 32, ...

Eine einfache Möglichkeit besteht darin, jedem Flag einen dezimalen Wert zuzuweisen und diese Werte - sobald das jeweilige Flag gesetzt werden soll - dem Rückgabewert hinzuzufügen (z.B. Flag1 und Flag5 setzen: Rückgabewert=WertVonFlag1+WertVonFlag5). Damit die Flags später einzeln abfragbar sind, muss verhindert werden, dass durch die Addition aus versehen der Status eines anderen Flags im Rückgabewert beeinflusst wird.

Die Flags dürfen daher nur die Werte 1, 2, 4, 8, 16, 32, ... (also immer das Doppelte des vorherigen Wertes beginnend bei 1) erhalten.

Inhalt von MyBatch_1.cmd:
01.
set _MyErrorCode=0 
02.
if exist "c:\MyFile1.txt" set /a "_MyErrorCode=_MyErrorCode + 1" &rem "Datei 1 gefunden"-Flag setzen 
03.
if exist "c:\MyFile2.txt" set /a "_MyErrorCode=_MyErrorCode + 2" &rem "Datei 2 gefunden"-Flag setzen 
04.
if exist "c:\MyFile3.txt" set /a "_MyErrorCode=_MyErrorCode + 4" &rem "Datei 3 gefunden"-Flag setzen 
05.
exit /b %_MyErrorCode%
Inhalt von MyBatch_2.cmd:
01.
call MyBatch_1.cmd 
02.
set _ReturnMask=%errorlevel% 
03.
::Die Flags müssen in der entgegengesetzten Reihenfolge abgefragt werden, also beginnend mit dem höchsten Wert! 
04.
call :FncGetState _ReturnMask 4 && echo Datei 3 wurde gefunden. 
05.
call :FncGetState _ReturnMask 2 && echo Datei 2 wurde gefunden. 
06.
call :FncGetState _ReturnMask 1 && echo Datei 1 wurde gefunden. 
07.
goto :EOF 
08.
 
09.
:FncGetState 
10.
  setlocal EnableDelayedExpansion & set _#EL=0 & set /a _#1="!%~1! - %~2" 
11.
  if %_#1% LSS 0 set _#EL=1 & set /a _#1+=%~2 
12.
 endlocal & set "%~1=%_#1%" & exit /b %_#EL%
Nachteil: Diese Lösung ist unflexibel, da die Funktion FncGetState den Status nur genau einmal zurückliefern kann (um die Abfrage des nächsten Flags vorzubereiten, löscht sie das aktuell abgefragte Flag aus _ReturnMask heraus). Zudem ist der Ansatz anfällig für Fehler: Wird in MyBatch_1.cmd ein weiteres (höherwertiges) Flag hinzugefügt und gesetzt, ohne MyBatch_2.cmd entsprechend anzupassen, so werden unter MyBatch_2.cmd plötzlich alle Flags wahr. Auch darf ein und dasselbe Flag unter Lösung 1 niemals zweimal gesetzt werden.

Lösung 2: Per Bit-Operation die Flag-Bits direkt setzen, löschen und abfragen

Um sämtliche Probleme, die Lösung 1 mit sich bringt, mit einem Schlag zu lösen, muss man ein Grundverständnis für Binärzahlen entwickeln.

Warum Binärzahlen? Der deutsche Erfinder Konrad Zuse entwickelt und baut das, was heute allgemein als Computer bezeichnet wird; genauer gesagt die erste vollautomatische, programmgesteuerte und frei programmierbare, in binärer Gleitkommarechnung arbeitende Rechenanlage. Diese trägt die Bezeichnung Z3 und wird 1941 fertiggestellt. Seine erste Rechenanlage, die 1938 veröffentlichte Z1, enthält bereits alle Basisbausteine eines modernen Computers, wie z.B. Leitwerk, Programmsteuerung, Speicher, Mikrosequenzen und Gleitkommarithmetik. Entgegen der größtenteils mit elektromechanischen Relais arbeitenden Z3 war die blecherne Technik der Z1 rein mechanischer Natur.

Welche Zustände konnte aber eine mechanische Wippe - und an dieser Idee angelehnt - ein elektromechanisches Reais in Zuses Rechenanlage annehmen?: Hoch und runter / an und aus / 1 und 0. Auch heute noch arbeiten Computer nach diesem Prinzip. Und es ist genau das, was wir hier benötigen: Wir wollen einzelne Flags setzen (1) oder löschen (0 - also als nicht gesetzt markieren).

Wenn ein Computer nur 1 und 0 kennt, wie kommt er dann auf eine Zahl wie 2, 3, 4, und 5, etc.? Er setzt mehrere Bits (ein Bit kann den Zustand 0 oder 1 annehmen) nebeneinander und rechnet sie nach seinem eigenen binären System zusammen:
01.
                                            0000 
02.
Bit3: ist es gesetzt, steht es für eine 8 <-'||'-> Bit0: ist es gesetzt, steht es für eine 1 
03.
Bit2: ist es gesetzt, steht es für eine 4 <--''--> Bit1: ist es gesetzt, steht es für eine 2
Mit vier Bits lassen sich die folgenden dezimalen Werte abbilden: 0 (binär: 0000), 1 (0001), 2 (0010), 3 (0011), 4 (0100), 5 (0101), 6 (0110), 7 (0111), 8 (1000), 9 (1001) bis 15 (1111). Eine Variable erhält in der Regel aber nicht nur 4 Bits, um ihren Wert zu verwalten, sondern wenigstens 8 Bits (ein Byte), meist 32 Bits (vier Byte) oder sogar 64 Bits (acht Byte). Die darstellbaren dezimalen Werte liegen bei nur einem Byte bereits bei 256 und nehmen mit jedem weiteren Byte rapide zu (zwei Byte=256*256=65536; drei Byte=256*256*256, etc.).

Mit jedem Bit, das dem Rückgabewert zur Verfügung steht, läßt sich ein Flag bedienen: Bit0 steht für unser erstes Flag, Bit1 für das zweite, etc. Lösung 1 ist bereits auf das Setzen binärer Flags ausgelegt – nur ist es dort nicht so offensichtlich. Sie nutzt einfach für jedes einzelne Flag keinen dezimalen Wert, der zwei oder mehr Bits gleichzeitig setzt. Pro Flag beschränkt sich das Beispiel zur Lösung 1 daher auf die dezimalen Werte 1 (binär: 0001), 2 (0010), 4 (0100) und 8 (1000), könnte aber auch über das vierte Bit hinaus gehen mit den Werten 16 (0001.0000), 32 (0010.0000), 64 (0100.0000), etc.

Der Rückgabewert hat gleich 32 Bits (0000.0000 0000.0000 0000.0000 0000.0000), die sich für unsere Flags nutzen lassen. Welche dezimalen Zahlen sich dahinter verbergen wird für unser Unterfangen irrelevant, sobald die Bits direkt gesetzt, gelöscht und abgefragt werden können. Genau das ermöglicht die folgende Funktion:
01.
:FncBit         ::Version 1.0.0 
02.
::Parameters:   <-s | -S | -c | -C | -f | -F>  <Var>  <Bit or BitMask to set, clear or find> 
03.
::Options:      -s set a bit;   -S set a bit-combination by mask 
04.
::              -c clear a bit; -C clear a bit-mask 
05.
::              -f find a bit;  -F find a bit-mask 
06.
::Example:      set /a _MyVar=0 & call :FncBit -s _MyVar 0 5  (set bit 0 and 5 in _MyVar; 00100001=33) 
07.
::              call :FncBit -s _MyVar 1                      (also set bit 1 in _MyVar;  00100011=35) 
08.
::              call :FncBit -c _MyVar 1                      (clear bit 1 in _MyVar;     00100001=33) 
09.
::              set a bit-combination by mask:      -S _MyVar 35   (set bits 0,1,5 in _MyVar) 
10.
::               also you can use a hex-format..    -S _MyVar 0x23 (set bits 0,1,5 in _MyVar) 
11.
::              find a bit ...    call :FncBit -f _MyVar 0 5    && goto :FoundBit0and5inMyVar 
12.
::              ... or bit-mask:  call :FncBit -F _MyVar 33     && goto :FoundBit0and5inMyVar 
13.
  set "_FncBit_Mode=%~1" & set "_FncBit_Var=%~2" & shift & shift 
14.
  :LOOP_FncBit 
15.
    if "%_FncBit_Mode%" == "-s" set /a %_FncBit_Var%"|=(1 << %~1)" 
16.
    if "%_FncBit_Mode%" == "-S" set /a %_FncBit_Var%"|=%~1" 
17.
    if "%_FncBit_Mode%" == "-c" set /a %_FncBit_Var%"&=(~(1 << %~1))" 
18.
    if "%_FncBit_Mode%" == "-C" set /a %_FncBit_Var%"&=(~ %~1)" 
19.
    if "%_FncBit_Mode%" == "-f" set /a _FncBit_GetBit"=%_FncBit_Var% & (1 << %~1)" 
20.
    if "%_FncBit_Mode%" == "-f" if "%_FncBit_GetBit%" == "0" exit /b 1 
21.
    if "%_FncBit_Mode%" == "-F" set /a _FncBit_GetBits"=%_FncBit_Var% & %~1" 
22.
    if "%_FncBit_Mode%" == "-F" set /a _FncBit_SearchBits"=%~1" &::ggf. hex nach dez umwandeln 
23.
    if "%_FncBit_Mode%" == "-F" if not "%_FncBit_GetBits%" == "%_FncBit_SearchBits%" exit /b 1 
24.
  if not "%~2" == "" shift && goto :LOOP_FncBit 
25.
 exit /b 0
Inhalt von MyBatch_1.cmd:
01.
set _MyErrorCode=0 
02.
if exist "c:\MyFile1.txt" call :FncBit -s _MyErrorCode 1 &rem "Datei 1 gefunden"-Flag setzen 
03.
if exist "c:\MyFile2.txt" call :FncBit -s _MyErrorCode 2 &rem "Datei 2 gefunden"-Flag setzen 
04.
if exist "c:\MyFile3.txt" call :FncBit -s _MyErrorCode 3 &rem "Datei 3 gefunden"-Flag setzen 
05.
goto :END 
06.
 
07.
<Hier muss die Funktion :FncBit hineinkopiert werden> 
08.
 
09.
:END 
10.
exit /b %_MyErrorCode%
Inhalt von MyBatch_2.cmd:
01.
call MyBatch_1.cmd 
02.
set _ReturnMask=%errorlevel% 
03.
::Die Flags können in beliebiger Reihenfolge beliebig oft und in beliebiger Kombination abgefragt werden: 
04.
call :FncBit -f _ReturnMask 1 && echo Datei 1 wurde gefunden. 
05.
call :FncBit -f _ReturnMask 2 3 && echo Datei 2 und 3 wurden gefunden. 
06.
call :FncBit -f _ReturnMask 2 3 || echo Datei 2 und/oder 3 wurde^(n^) nicht gefunden. 
07.
 
08.
rem Die Option -F, statt -f, ermöglicht die Angabe einer Bit-Kombination als einzelnen Wert: 
09.
rem Bit0 steht dezimal für 1, Bit1 für 2, Bit2 für 4 und Bit3 für 8, etc. 
10.
rem Im Folgenden soll geprüft werden, ob Bit2 und Bit3 gesetzt wurde: 4+8=12 
11.
call :FncBit -F _ReturnMask 12 && echo Datei 2 und 3 wurden gefunden. 
12.
 
13.
goto :EOF 
14.
 
15.
<Hier muss die Funktion :FncBit hineinkopiert werden>

3. Weiterführende Tipps im Umgang mit Flags

Den Bits einen sprechenden Namen geben

Ziel ist es, statt
    call :FncBit -s Variable 2
für das Setzen von Bit2 in der Variablen eine lesbare Syntax zu verwenden. Denn die "2" sagt nicht aus, wofür das Bit steht. In unserem Beispiel von oben, indem Bit2 für "ich habe Datei 2 gefunden" steht, wäre eine lesbare Syntax wie folgt denkbar:
    call :FncBit -s Variable %Flag#FoundFile2%
Siehe dazu den folgenden Quellcode:
01.
@echo off & setlocal 
02.
rem Die folgende Zeile sollte sowohl in MyBatch_1.cmd, als auch in MyBatch_2.cmd eingefügt werden: 
03.
call :FncEnum Flag UnexpectedError FoundFile1 FoundFile2 FoundFile3 
04.
... 
05.
rem Flag setzen (in MyBatch_1.cmd): 
06.
if exist "c:\MyFile2.txt" call :FncBit -s _MyErrorCode %Flag#FoundFile2% 
07.
... 
08.
rem Flag abfragen (in MyBatch_2.cmd): 
09.
call :FncBit -f _ReturnMask %Flag#FoundFile2% && echo Datei 2 wurde gefunden. 
10.
... 
11.
goto :EOF 
12.
 
13.
rem Die folgende Funktion sollte sowohl in MyBatch_1.cmd, als auch in MyBatch_2.cmd eingefügt werden: 
14.
:FncEnum        ::Version 1.0.0 
15.
::Parameters:   [-a] <Prefix> <Name1> <Name2> .. 
16.
::Example:      call :FncEnum Enum1 a b & call :FncEnum Enum1 -a c d e f & call :FncEnum Enum2 c d e f 
17.
::              echo %Enum1#a% %Enum1#b% %Enum1#c% %Enum1#d% %Enum1#e% %Enum1#f% (out: 0 1 2 3 4 5) 
18.
::              echo %Enum2#c% %Enum2#d% %Enum2#e% %Enum2#f%                     (out: 0 1 2 3) 
19.
::              echo next free enum of Enum1=%Enum1#% and Enum2=%Enum2#%         (out: 6 and 4) 
20.
  if not "%~1" == "-a" ( set "%~1#=0" ) else ( shift ) 
21.
  set "_FncEnum_Prefix=%~1#" & shift 
22.
  :LOOP_FncEnum_SetEnum 
23.
    set /a "%_FncEnum_Prefix%%~1=%_FncEnum_Prefix%" & set /a "%_FncEnum_Prefix%+=1" & shift  
24.
  if not "%~1" == "" goto :LOOP_FncEnum_SetEnum 
25.
 exit /b 0

Bit0 als allgemeines Fehlerbit verwenden

In nahezu allen Anwendungsfällen bietet sich ein Flag für den allgemeinen Fehlerzustand bzw. für den "unerwarteten (innerhalb der Batch nicht näher spezifizierten) Fehler" an. Bit0 ist ideal dafür, weil das Flag unabhängig von der Anzahl der übrigen Flags und der Variablengröße immer am selben Platz zu finden ist.

Bye, nz
Mitglied: NeonZero
22.09.2008 um 18:36 Uhr
In der DOS-Hilfe zu „set /?“ ist ein Hinweis von MS zu finden, dass set /a neben dezimalen Zahlen und hex (0x<hex-Wert>) bzw. okt (0<okt-Wert>) auch mit der binären Schreibweise zurechtkommt (0b<bin-Wert>). Das mit den binären Zahlen wollte mir nicht gelingen. Leider konnte ich dazu im Netz nichts finden. Hat das jemand von euch schon einmal geschafft? Wenn ja, wie? Ein kleiner Code-Schnipsel wäre nicht schlecht, damit ich das in die Anleitung einarbeiten kann.

Bye, nz
Bitte warten ..
Neuester Wissensbeitrag
Internet

Unbemerkt - Telekom Netzumschaltung! - BNG - Broadband Network Gateway

(3)

Erfahrungsbericht von ashnod zum Thema Internet ...

Ähnliche Inhalte
Batch & Shell
gelöst Mehrere PDF-Dateien per Batch zusammenfügen (1)

Frage von Grimmli zum Thema Batch & Shell ...

Batch & Shell
gelöst Batch um mehrere IP-Adressen via Ping auf Erreichbarkeit zu prüfen (5)

Frage von Galindiesel zum Thema Batch & Shell ...

Heiß diskutierte Inhalte
Switche und Hubs
Trunk für 2xCisco Switch. Wo liegt der Fehler? (17)

Frage von JayyyH zum Thema Switche und Hubs ...

Windows Server
Outlook Verbindungsversuch mit Exchange (15)

Frage von xbast1x zum Thema Windows Server ...

DSL, VDSL
DSL-Signal bewerten (14)

Frage von SarekHL zum Thema DSL, VDSL ...