VBScript setzt Umgebungsvariable - Verarbeitung im Batch funktioniert nur bedingt
Hallo Forum,
ich versuche mich gerade an einem VBS-Tool, das die Zeit vom Start des Tools in Sekunden hochzählt und in eine Umgebungsvariable schreibt. Das ganze hat den Sinn, die Laufzeit von einzelnen (langwierigeren) Batchsteps anzuzeigen.
Grundsätzlich funktioniert das ganze auch hervorragend. Beim ersten Start wird ein temporäres VBScript in Temp geschrieben, das die Arbeit übernimmt. Beim erneuten Aufruf wird dieses Script geschlossen, gelöscht und die Umgebungsvariablen gelöscht.
Hier erst einmal die Datei secdiff.vbs:
Wie gesagt, das Script macht seine Arbeit gut. Wenn ich es händisch starte und anschließend die Variable %secdiff% in einem Batch abfrage, erhalte ich die verstrichene Zeit in Sekunden.
Das Problem liegt darin, dass bei mehrmaligem Aufruf der Variablen %secdiff% zu Laufzeit des Batch, keine Änderung des Werts zu beobachten ist. Es scheint als würde die Variable nur einmal expandiert.
Beispiel *.bat:
Erwarten würde ich jetzt:
Leider bleibt es durchgängig bei "ECHO ist ausgeschaltet (OFF).", obwohl das VBScript ordnungsgemäß läuft.
Was kann ich tun?
Vielen Dank im Voraus.
Grüße
rubberman
ich versuche mich gerade an einem VBS-Tool, das die Zeit vom Start des Tools in Sekunden hochzählt und in eine Umgebungsvariable schreibt. Das ganze hat den Sinn, die Laufzeit von einzelnen (langwierigeren) Batchsteps anzuzeigen.
Grundsätzlich funktioniert das ganze auch hervorragend. Beim ersten Start wird ein temporäres VBScript in Temp geschrieben, das die Arbeit übernimmt. Beim erneuten Aufruf wird dieses Script geschlossen, gelöscht und die Umgebungsvariablen gelöscht.
Hier erst einmal die Datei secdiff.vbs:
timev = Now
Dim oSh, oEnv
Set oSh = CreateObject("WScript.Shell")
Set oEnv = oSh.Environment("volatile")
temp = osh.ExpandEnvironmentStrings("%temp%")
FileName = temp & "\secdifftemp.vbs"
If oEnv("secdiff") = "" Then
oEnv("secdiff") = 0
vbsContents = "Dim oSh, oEnv" & vbCrLf
vbsContents = vbsContents & "Set oSh = CreateObject(""WScript.Shell"")" & vbCrLf
vbsContents = vbsContents & "Set oEnv = oSh.Environment(""volatile"")" & vbCrLf
vbsContents = vbsContents & "Set oSh = Nothing" & vbCrLf
vbsContents = vbsContents & "Do" & vbCrLf
vbsContents = vbsContents & "stimediff = DateDiff(""s"", WScript.Arguments(0), Now)" & vbCrLf
vbsContents = vbsContents & "oEnv(""secdiff"") = stimediff" & vbCrLf
vbsContents = vbsContents & "WScript.Sleep 500" & vbCrLf
vbsContents = vbsContents & "Loop"
Set oFile = CreateObject("Scripting.FileSystemObject").OpenTextFile(FileName, 2, True)
oFile.Write vbsContents
Set oFile = Nothing
Dim oExec
Set oExec = oSh.Exec("wscript.exe" & " """ & FileName & """ " & """" & timev & """")
secdifftempID = oExec.ProcessID
Set oExec = Nothing
oEnv("secdifftempID") = secdifftempID
Else
secdifftempID = oEnv("secdifftempID")
Dim results
Set results = GetObject("winmgmts:\\.\root\cimv2").ExecQuery("Select * from Win32_Process where ProcessId = '" & secdifftempID & "'")
For Each obj In results
obj.Terminate()
Next
Set results = Nothing
WScript.Sleep 200
oEnv.Remove "secdiff"
oEnv.Remove "secdifftempID"
Dim oFs
Set oFs = CreateObject("Scripting.FileSystemObject")
oFs.GetFile(FileName).Delete
Set oFs = Nothing
End If
Set oEnv = Nothing
Set oSh = Nothing
Wie gesagt, das Script macht seine Arbeit gut. Wenn ich es händisch starte und anschließend die Variable %secdiff% in einem Batch abfrage, erhalte ich die verstrichene Zeit in Sekunden.
Das Problem liegt darin, dass bei mehrmaligem Aufruf der Variablen %secdiff% zu Laufzeit des Batch, keine Änderung des Werts zu beobachten ist. Es scheint als würde die Variable nur einmal expandiert.
Beispiel *.bat:
@echo off &setlocal
echo %secdiff%
secdiff
echo %secdiff%
ping -n 6 localhost>nul
echo %secdiff%
secdiff
echo %secdiff%
pause
Erwarten würde ich jetzt:
ECHO ist ausgeschaltet (OFF).
0
5
ECHO ist ausgeschaltet (OFF).
Drücken Sie eine beliebige Taste . . .
Leider bleibt es durchgängig bei "ECHO ist ausgeschaltet (OFF).", obwohl das VBScript ordnungsgemäß läuft.
Was kann ich tun?
Vielen Dank im Voraus.
Grüße
rubberman
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 122448
Url: https://administrator.de/contentid/122448
Ausgedruckt am: 21.11.2024 um 21:11 Uhr
6 Kommentare
Neuester Kommentar
Moin rubberman,
mit dem Befehl 'Set oSh = CreateObject("WScript.Shell") ' erzeugst Du ja zwangsweise nur eine vom Parent-Prozess abgeleitete Tochter-Instanz der laufenden CMD-Session.
Bedeutet, dass für die neue instanz alle Umgebungsvariablen gelten, die auch der ober-Instanz bekannt sind.
Genau dieses nutzt Du ja auch beim Auslesen/Expandieren der %temp%-Variablen.
Dennoch steht Dir "nur" eine Kopie des gesamten Environments zur verfügung, nicht etwa das Original.
Und die Kopie kannst Du natürlich ändern, erweitern oder anpassen wiedewutt, aber Du nimmst diese geänderte Kopie mit ins virtuelle Grab, wenn der Tochterprozess beendet wird.
Ob nun mit ' Set oEnv = Nothing:.Set oSh = Nothing' oder ohne diese beiden Zeilen ist unerheblich.
Umgehungsmöglichkeit: Übergib die %secdiff%-Werte nicht als Umgebungsvariable, sondern transportiere diese Info via Wscript.echo raus aus dem VBS-Schnipsel und rein in den Batch mit einer FOR/F-Anweisung.
Dafür haben wir ein oder zwei Beispiele im Bereich "Batch & Shell".
Grüße
Biber
mit dem Befehl 'Set oSh = CreateObject("WScript.Shell") ' erzeugst Du ja zwangsweise nur eine vom Parent-Prozess abgeleitete Tochter-Instanz der laufenden CMD-Session.
Bedeutet, dass für die neue instanz alle Umgebungsvariablen gelten, die auch der ober-Instanz bekannt sind.
Genau dieses nutzt Du ja auch beim Auslesen/Expandieren der %temp%-Variablen.
Dennoch steht Dir "nur" eine Kopie des gesamten Environments zur verfügung, nicht etwa das Original.
Und die Kopie kannst Du natürlich ändern, erweitern oder anpassen wiedewutt, aber Du nimmst diese geänderte Kopie mit ins virtuelle Grab, wenn der Tochterprozess beendet wird.
Ob nun mit ' Set oEnv = Nothing:.Set oSh = Nothing' oder ohne diese beiden Zeilen ist unerheblich.
Umgehungsmöglichkeit: Übergib die %secdiff%-Werte nicht als Umgebungsvariable, sondern transportiere diese Info via Wscript.echo raus aus dem VBS-Schnipsel und rein in den Batch mit einer FOR/F-Anweisung.
Dafür haben wir ein oder zwei Beispiele im Bereich "Batch & Shell".
Grüße
Biber
Hallo rubberman und Biber!
Was spricht eigentlich gegen die Verwendung einer Datei, um den Start-Timestamp festzuhalten (eine Temporärdatei mehr sollte ja keinen großen Unterschied machen)?
Dann würde eigentlich etwas in der Art genügen:
Soferne die hier nur durch "ping"-Pausen angedeuteten Aktionen weitere Batches sind, übernehmen diese bei einem Aufruf mit "call" ja die Variablen %S% und %T% (der Inhalt Letzterer könnte als zusätzliche Vereinfachung gleich in das VBScript als Konstante eingetragen werden), und es "kostet" also jeweils nur die eine "for /f"-Zeile im aufgerufenen Batch, um die (aktualisierte) Variable %secdiff% auch dort zu erhalten ...
Grüße
bastla
Was spricht eigentlich gegen die Verwendung einer Datei, um den Start-Timestamp festzuhalten (eine Temporärdatei mehr sollte ja keinen großen Unterschied machen)?
Dann würde eigentlich etwas in der Art genügen:
@echo off & setlocal
set "T=%temp%\Timestamp.txt"
set "S=%temp%\Secdiff.vbs"
>%S% echo WScript.Echo DateDiff("s",CreateObject("Scripting.FileSystemObject").OpenTextFile(WScript.Arguments(0)).ReadAll,Now)
::Start
>%T% echo %date%%time:~,8%
ping -n 6 localhost>nul
for /f %%i in ('cscript //nologo %S% %T%') do set "secdiff=%%i"
echo %secdiff%
ping -n 10 localhost>nul
for /f %%i in ('cscript //nologo %S% %T%') do set "secdiff=%%i"
echo %secdiff%
::Ende
del %S%
del %T%
Grüße
bastla
Hallo rubberman!
Was die Umgebungsvariablen angeht, liegst Du falsch.
Das Master-Environ (1.Instanz) kannst Du z.B. auf der Console nur mit einem direkten SET-Befehl ändern. Sobald eine Batch, ein Programm etc. aufgerufen wird, steht immer nur eine Environ-Kopie des Aufrufers zur Verfügung. D.h. im Grunde wird das Environ von Instanz zu Instanz vererbt und wenn eine Instanz beendet wird, existiert die jeweilige Environ-Kopie nicht mehr.
Das kannst Du ganz einfach testen, indem Du z.B. auf der Console Command oder Cmd eingibst und eine SET-Variable definierst und diesen Vorgang mehrmals wiederholst und am Ende wieder mit dem Befehl Exit von der jeweils letzten Instanz in die vorige Instanz wechselst.
Den Grund dafür ist, dass das Environ pro Instanz maximal 32KB groß sein darf, aber nur einen Speicherblock mit der aktuellen Größe belegt. Das wurde mal in DOS so festgelegt und wurde zwecks Kompatibilität so aufrecht erhalten.
Beim setzen einer oder mehreren Variablen mit SET, muss im Prinzip ein neuer Speicherblock angelegt werden und hier fangen die Probleme an. Wenn ein Programm geladen wird, dann benötigt es bestimmte Informationen. D.h. einem Programm wird eine sogenannte Programm-Segemnt-Präfix (PSP) vorangestellt. Bei einer *.COM währen das z.B. die ersten 256 Byte zusätzlich zum eigentlichen Programmcode. In der PSP befindet sich dann u.a. die Adresse des Environ-Speicherblocks.
Wenn jetzt z.B. ein Programm geladen wird, das wiederum ein anderes Programm oder eine Command-Funktion aufruft, müsste die PSP aller bereits geladenen Programme neu initialisiert werden und das währe wiederum schwierig, da z.B. eine *.EXE noch einen unterschiedlich großen Header mit Adressen für die Speichermodell-Initialisierung enthält....
Command/Cmd-Beispiel:
Set Instanz_1=1
Command
Set Instanz_2=2
Command
Set Instanz_3=3
Ergebnis:
Set -> Instanz_1=1 + Instanz_2=2 + Instanz_3=3
Exit
Set -> Instanz_1=1 + Instanz_2=2
Exit
Set -> Instanz_1=1
Die Variablen %Date% und %Time% und %Random% werden von der Cmd direkt erzeugt und haben mit dem Environ nix am Hut
Gruß Dieter
Was die Umgebungsvariablen angeht, liegst Du falsch.
Das Master-Environ (1.Instanz) kannst Du z.B. auf der Console nur mit einem direkten SET-Befehl ändern. Sobald eine Batch, ein Programm etc. aufgerufen wird, steht immer nur eine Environ-Kopie des Aufrufers zur Verfügung. D.h. im Grunde wird das Environ von Instanz zu Instanz vererbt und wenn eine Instanz beendet wird, existiert die jeweilige Environ-Kopie nicht mehr.
Das kannst Du ganz einfach testen, indem Du z.B. auf der Console Command oder Cmd eingibst und eine SET-Variable definierst und diesen Vorgang mehrmals wiederholst und am Ende wieder mit dem Befehl Exit von der jeweils letzten Instanz in die vorige Instanz wechselst.
Den Grund dafür ist, dass das Environ pro Instanz maximal 32KB groß sein darf, aber nur einen Speicherblock mit der aktuellen Größe belegt. Das wurde mal in DOS so festgelegt und wurde zwecks Kompatibilität so aufrecht erhalten.
Beim setzen einer oder mehreren Variablen mit SET, muss im Prinzip ein neuer Speicherblock angelegt werden und hier fangen die Probleme an. Wenn ein Programm geladen wird, dann benötigt es bestimmte Informationen. D.h. einem Programm wird eine sogenannte Programm-Segemnt-Präfix (PSP) vorangestellt. Bei einer *.COM währen das z.B. die ersten 256 Byte zusätzlich zum eigentlichen Programmcode. In der PSP befindet sich dann u.a. die Adresse des Environ-Speicherblocks.
Wenn jetzt z.B. ein Programm geladen wird, das wiederum ein anderes Programm oder eine Command-Funktion aufruft, müsste die PSP aller bereits geladenen Programme neu initialisiert werden und das währe wiederum schwierig, da z.B. eine *.EXE noch einen unterschiedlich großen Header mit Adressen für die Speichermodell-Initialisierung enthält....
Command/Cmd-Beispiel:
Set Instanz_1=1
Command
Set Instanz_2=2
Command
Set Instanz_3=3
Ergebnis:
Set -> Instanz_1=1 + Instanz_2=2 + Instanz_3=3
Exit
Set -> Instanz_1=1 + Instanz_2=2
Exit
Set -> Instanz_1=1
Die Variablen %Date% und %Time% und %Random% werden von der Cmd direkt erzeugt und haben mit dem Environ nix am Hut
Gruß Dieter