Gibt es einen eleganten Weg die Environment-Variable die mit einer
Batch-Datei zusätzlich angelegt/veraendert wurden in python einzulesen?
und stdout und stderr als Strings/Liste zu bekommen?
z.B. diese VS2019 bat-Datei erzeugt ein Build-Environment (VS2008,
VS2010 usw. legen andere Variable an)
und ganz vergessen: an den Errorlevel von der Batch-Datei muss ich auch
noch ran :/
hier ein batch-test der alle Daten auspuckt die ich so brauche - ich
hoffe
mein stdout/stderr schreiben ist so richtig
1
@echooff
2
setneue_var=neuer_wert
3
setpath=%path%;neuer_wert
4
echo"stdout"1>stdout
5
echo"stderr"2>stderr
6
exit/b123
geht das mit popen oder muss ich da wirklich noch eine batch-Datei
der Trick mit dem & und set in Datei umleiten geht, der call muss
scheinbar sein damit nicht zu früh evaluiert wird, aber komme so nicht
mehr an den 123 Error-Level dran
Dirk schrieb:> Wieso so umständlich die Environmentvariablen prüfen? Die Werte sind> doch bekannt oder?
Nein nicht wirklich, abhängig von Version, Enterprise, Community etc.
und OS-Sprache etc. sind die anders (zwischen 20-40 Variablen werden
erzeugt, teilweise verändert)
ich muss hier VS2008 (WinCE builds) bis VS2019 unter Kontrolle halten
und die liefern jeweils ihre eigene Standard-Batch Datei mit - jede ein
bisschen anders - mit Batchdateien funktioniert das super aber ich würde
das gerne nach Python konvertieren
da gibs libs für schrieb:> Inwiefern hilft da der Dictos.environ> nicht weiter?>> Oder sollen die .bat selbst ihre Umgebung ausgeben?
stell dir vor du hast diese Bat-Datei, die du nicht änder darfst
(weil sie von Microsoft kommt) und die legt ca. 20-40 Env-Vars an die du
für irgendwelche Microsoft-Produkte in folge brauchst - der Inhalt der
Vars und auch die Vars sind für dich persönlich völlig unrelevant, aber
Microsoft-Tools brauchen die trotzdem
1
@echooff
2
echofirstparam:%1
3
setneue_var=neuer_wert
4
setpath=%path%;neuer_wert
5
echofromstdout
6
echofromstderr1>&2
7
exit/b123
ich brauche den stdout und den stderr
die errorlevel result 123
und das komplette environment das diese Batch erzeugt hat
in Python
stdout, stderr und das environment bekomme ich
nur der errorlevel 123 will nicht erscheinen - wohl wegen
der & Verknüpfung
eigentlich fehlt mir nur noch der errorlevel
oder jemand zeigt mir wie das viel eleganter geht
andere Idee wäre noch eine batch zu haben welche das
alles intern macht und entsprechend Ausgaben hat - problem
ich habe viele Scripte und möchte nicht unbedingt für jedes andere
Anzahl an Parametern eine weitere batch-Datei schreiben
Ich würde es ein kleines bisschen anders machen.
1. Alle Env vars einlesen
https://gist.github.com/bthaman/f7b2b8db006313f73074fe3dcebf0952
2. batch file starten
3. Schritt 1 wieder und dann die beiden Dictionaries vergleichen, dann
müsstest Du die Unterschiede bekommen, oder?
Hiermit bekomme ich den returncode der bat datei
Dirk schrieb:> Ich würde es ein kleines bisschen anders machen.>> Alle Env vars einlesen> https://gist.github.com/bthaman/f7b2b8db006313f73074fe3dcebf0952> batch file starten> Schritt 1 wieder und dann die beiden Dictionaries vergleichen, dann> müsstest Du die Unterschiede bekommen, oder?
Das filter der geaenderten,neuen mache ich schon, das geht ja einfach
wenn man das environment hat
>> Hiermit bekomme ich den returncode der bat datei> import subprocess> import os> test_bat = "d:\\test.bat"> retcode = subprocess.call(test_bat)> print (retcode)
So bekomme ich den errorlevel auch - das funktioniert, aber es kommt nur
0 wenn man den Aufrufe und set kombiniert
Wie schaffst du es das environment abzufragen bevor der cmd zu geht?
Das Environment wird ja nicht global verändert sondern nur für den call
Aufruf, danach ist das Environment doch genau so wie vorher, oder?
cppbert3 schrieb:> Wie schaffst du es das environment abzufragen bevor der cmd zu geht?> Das Environment wird ja nicht global verändert sondern nur für den call> Aufruf, danach ist das Environment doch genau so wie vorher, oder?
In https://docs.python.org/3/library/os.html
steht bei os.environ "This mapping is captured the first time the os
module is imported, typically during Python startup as part of
processing site.py. Changes to the environment made after this time are
not reflected in os.environ, except for changes made by modifying
os.environ directly."
Aber sorry - k.A. wie man os.environ aktualisiert. Wird aber wohl
irgendwie gehen ;)
Christian U. schrieb:> cppbert3 schrieb:>>> Wie schaffst du es das environment abzufragen bevor der cmd zu geht?>> Das Environment wird ja nicht global verändert sondern nur für den call>> Aufruf, danach ist das Environment doch genau so wie vorher, oder?>> In https://docs.python.org/3/library/os.html> steht bei os.environ "This mapping is captured the first time the os> module is imported, typically during Python startup as part of> processing site.py. Changes to the environment made after this time are> not reflected in os.environ, except for changes made by modifying> os.environ directly."> Aber sorry - k.A. wie man os.environ aktualisiert. Wird aber wohl> irgendwie gehen ;)
Wie schon 3 mal gepostet, das environment bekomme ich über umwege genau
mit den infos die ich brauche, nur der returncode ist dann immer 0
Ich habe kein problem irgendwelche environment variablen kit python zu
lesen, oder programme auszuführen, nur batchdateien die das environment
erweitern + der returncode ist nicht ganz so einfach
Eine .bat-Datei wird von einem Programm interpretiert. Unter Unix ist
das eine Shell, unter Windows der Kommandointerpreter (ich glaub
command.com). Das Programm wird in einem Prozess ausgeführt, der auch
das Environment beherbergt. Wenn du eine .bat-Datei von Python aus
ausführen willst musst du einen neuen Prozess anlegen und darin den
Kommandointerpreter ausführen, der dann die .bat-Datei interpretiert.
Dieser neue Prozess hat aber ein eigenständiges Environment, das durch
die .bat-Befehle zwar ergänzt wird, auf dass der Vaterprozess (Python)
aber keinen Zugriff hat.
Du musst das neue Environment also nach der Ausführung der .bat-Datei
und vor der Beendigung des Kommandointerpreters irgendwie anders vom
Kindprozess zum Vaterprozess übertragen.
Da gibt es mehrere Möglichkeiten, aber eine Datei ist mit die
Einfachste.
LG, Sebastian
Wenn der erste Parameter von Popen ein String ist, dann führt Python den
Kommandointerpreter in dem neuen Prozess aus und übergibt den String als
Kommandozeile.
In deinem Versuch oben trennst du mehrere Kommando mit &. Die werden
dann als Hintergrundprozesse gestartet anstatt auf ihre Beendigung zu
warten. Stattdessen solltest du die Kommandos mit ; trennen. Dann steht
dir auch der Returncode der Ausführung der .bat-Datei zur Verfügung, der
ansonsten verfällt.
LG, Sebastian
Sebastian schrieb:> Eine .bat-Datei wird von einem Programm interpretiert. Unter Unix> ist das eine Shell, unter Windows der Kommandointerpreter (ich glaub> command.com). Das Programm wird in einem Prozess ausgeführt, der auch> das Environment beherbergt. Wenn du eine .bat-Datei von Python aus> ausführen willst musst du einen neuen Prozess anlegen und darin den> Kommandointerpreter ausführen, der dann die .bat-Datei interpretiert.> Dieser neue Prozess hat aber ein eigenständiges Environment, das durch> die .bat-Befehle zwar ergänzt wird, auf dass der Vaterprozess (Python)> aber keinen Zugriff hat.> Du musst das neue Environment also nach der Ausführung der .bat-Datei> und vor der Beendigung des Kommandointerpreters irgendwie anders vom> Kindprozess zum Vaterprozess übertragen.> Da gibt es mehrere Möglichkeiten, aber eine Datei ist mit die> Einfachste.> LG, Sebastian
Das ist 100% klar und ich mache es auch mit einer Datei, was auch
klappt, nur bekomme ich noch nicht den errorlevel aus der bat datei
Mein Code zeigt das doch deutlich oder bin ich da zu ungenau?
Sebastian schrieb:> Wenn der erste Parameter von Popen ein String ist, dann führt> Python den Kommandointerpreter in dem neuen Prozess aus und übergibt den> String als Kommandozeile.> In deinem Versuch oben trennst du mehrere Kommando mit &. Die werden> dann als Hintergrundprozesse gestartet anstatt auf ihre Beendigung zu> warten. Stattdessen solltest du die Kommandos mit ; trennen. Dann steht> dir auch der Returncode der Ausführung der .bat-Datei zur Verfügung, der> ansonsten verfällt.> LG, Sebastian
Werde ich gleich morgen früh testen, Danke
cppbert3 schrieb:> Sebastian schrieb:>>> Wenn der erste Parameter von Popen ein String ist, dann führt>> Python den Kommandointerpreter in dem neuen Prozess aus und übergibt den>> String als Kommandozeile.>> In deinem Versuch oben trennst du mehrere Kommando mit &. Die werden>> dann als Hintergrundprozesse gestartet anstatt auf ihre Beendigung zu>> warten. Stattdessen solltest du die Kommandos mit ; trennen. Dann steht>> dir auch der Returncode der Ausführung der .bat-Datei zur Verfügung, der>> ansonsten verfällt.>> LG, Sebastian>> Werde ich gleich morgen früh testen, Danke
Ist ; nicht nur für Parametertrennung nutzbar?
Bei der bash ist es ; aber unter Windows Cmd ist Verkettung mit &
cppbert3 schrieb:> Ist ; nicht nur für Parametertrennung nutzbar?> Bei der bash ist es ; aber unter Windows Cmd ist Verkettung mit &
Mmh, unter Windows rutsche ich etwas. Da sind ja mehrere Ebenen
involviert, Popen könnte da auch noch mitmischen. Mir kommt es aber so
vor dass du ganz kurz vor einer Lösung bist ...
LG, Sebastian
Sebastian schrieb:> Mir kommt es aber so> vor dass du ganz kurz vor einer Lösung bist ...
nicht wirklich :(
ich hab keine Problem mit Python, Environment-Variablen. Prozessen usw.
nur dieses kleine Detail mit dem Return-Code bei verketteten Aufrufen
bekomme ich bis jetzt noch nicht hin
cppbert3 schrieb:> Sebastian schrieb:>> Mir kommt es aber so>> vor dass du ganz kurz vor einer Lösung bist ...>> nicht wirklich :(>> ich hab keine Problem mit Python, Environment-Variablen. Prozessen usw.> nur dieses kleine Detail mit dem Return-Code bei verketteten Aufrufen> bekomme ich bis jetzt noch nicht hin
Ich deute das wie folgt: popen() bekommt den exitcode vom
bat-Interpreter (command.com) zurück weil dies sein Kindsprozess ist.
Wert 0 weil fehlerfrei abgeschlossen.
Der errorlevel der bat (123) bleibt aus Mikrosoftschen Gründen im
bat-Interpreter hängen... :-(
Resp. Wenn "set" der LETZTE Befehl ist der ERFOLGREICH ausgeführt wird,
hinterlässt der batt-Interpreter eben DESSEN Exitcode.
Wie muss der bat-Interpreter beendet werden, um einen beliebigen
errolevel (einer .bat) als eigener exitcode beim beenden zu
hinterlassen?
da gibs libs für schrieb:> Resp. Wenn "set" der LETZTE Befehl ist der ERFOLGREICH ausgeführt wird,> hinterlässt der batt-Interpreter eben DESSEN Exitcode.>> Wie muss der bat-Interpreter beendet werden, um einen beliebigen> errolevel (einer .bat) als eigener exitcode beim beenden zu> hinterlassen?
ich hab das Problem auch nur auf der Konsole "nachvollziehen" können und
daraus einen neuen Post gemacht
Beitrag "Win7x64/DOS/CMD-Batch: %errorlevel% kommt "manchmal" richtig raus?"
irgendwie ist das nicht so 100% deterministisch
und in Python mit popen bekomme ich auch diese Effekte - also erst mal
unter pure CMD verstehen warum das so ist - vielleicht löst sich dann
mein Python-Problem automatisch mit
da gibs libs für schrieb:> Resp. Wenn "set" der LETZTE Befehl ist der ERFOLGREICH ausgeführt wird,> hinterlässt der batt-Interpreter eben DESSEN Exitcode.
das löse ich durch dieses zwischen-echo in eine Datei - und das klappt
auch "meistens" auf der Konsole -> siehe den anderen Post
Sebastian W. schrieb:> Ich glaube ich hab eine Lösung.> Dann brauchst du noch eine zweite Batchdatei popen.bat:@echo off> call %1 %2 %3 %4 %5 %6 %7 %8 %9> echo %errorlevel% >ret.txt> set >env.txt
hatte die Hoffnung auf eine weitere Batch zu verzichten - aber %1 %2 ...
usw sollte für meine Anforderung (mit 1-5,6 Parameter) auch reichen -
danke für den Gehirn-Short-Cut damit ich nicht weiter suche
Ich teste das jetzt mal ordentlich
> call %1 %2 %3 %4 %5 %6 %7 %8 %9
Das versagt doch schon bei Leerzeichen in den Parametern oder
mehr als neun Argumenten.
Du solltest dir bessere Ratgeber suchen, die z.B. nicht command.com
fuer die ausfuehrende Interpreterinstanz haelt.
Lustigerweise gibt es fuer WinCE ein command.com.
Wenn man %ERRORLEVER% in einem Script auslesen will, macht man
das ganz einfach so:
1
if errorlevel 255 goto l255
2
if errorlevel 254 goto l254
3
if errorlevel 253 goto l253
4
...
5
if errorlevel 2 goto l2
6
if errorlevel 1 goto l1
7
if errorlevel 0 goto l0
8
echo no match
9
goto ex
10
11
:L255
12
echo 255
13
goto ex
14
:L254
15
echo 254
16
goto ex
17
:L253
18
echo 253
19
goto ex
20
...
21
:L2
22
echo 2
23
goto ex
24
:L1
25
echo 1
26
goto ex
27
:L0
28
echo 0
29
goto ex
30
31
:ex
Werte groesser als 256 gehn halt nicht.
-1 kann ja nicht vorkommen :-).
Obiges Skript ist im uebrigen bei mir 30 Jahre alt.
In einem meiner letzen Projekte musste ich einer anderen Abteilung
2 Skipte zuarbeiten. Wenn ich da nach "Python" oder "Shell" verlangt
haette, waere das ganze genau an der Stelle gestorben.
Da wurde kraeftig mit BACKQUOTE und Quoting gearbeitet.
Solange das bei dir nicht traumhaft sicher sitzt, vergiss das ganze.
Denk mal drueber nach!
oerks schrieb:> Das versagt doch schon bei Leerzeichen in den Parametern oder> mehr als neun Argumenten.
dann würde ich in dem Fall das "call %1 %2 %3..."-Script in Python
generieren - mehr als trivial und dann habe ich keine Probleme mit
Parameter-Anzahl und Quoting
oerks schrieb:> Du solltest dir bessere Ratgeber suchen, die z.B. nicht command.com> fuer die ausfuehrende Interpreterinstanz haelt.> Lustigerweise gibt es fuer WinCE ein command.com.
die Microsoft-Tools die ich verwenden muss interessieren sich nicht
dafür was ich für sinnvoll halte :)
oerks schrieb:> Wenn man %ERRORLEVER% in einem Script auslesen will, macht man> das ganz einfach so:
das ist absolut klar - seit mind. 25 Jahren :) - es geht nur darum ein
paar Batch-Scripte in einen viel größeren Python-Code Verbund zu
integrieren
oerks schrieb:> Da wurde kraeftig mit BACKQUOTE und Quoting gearbeitet.> Solange das bei dir nicht traumhaft sicher sitzt, vergiss das ganze.>> Denk mal drueber nach!
keine Ahnung was du mir sagen willst - sind nicht meine Batch-Dateien
sondern von Microsoft, ich mag kein CMD-Batch wegen dem ganzen Quoting
Chaos und der eingeschränkten Funktionalität (verglichen mit Python,
Powershell, selbst Bash ist besser), ist nicht portable, hier gibt es
einen Haufen Python-Code wo die Batch-Dateien nur stören, je weniger
Batch umso besser
cppbert3 schrieb:> path=os.path.abspath(os.path.dirname(_file_))>> test_bat_path=path+"\\test.bat"> error_level_path=path+"\\error_level.txt"> env_path=path+"\\env.txt">> cmd = test_bat_path + " param1 & echo %errorlevel% > " +> error_level_path + " & set > "+env_path;>> print("cmd: "+cmd)>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
Statt "+" zur Konkatenation Deiner Pfade solltest Du die Pfadtrenner
lieber weglassen und die Funktion os.path.join benutzen. Also statt
'test_bat_path=path+"\\test.bat"' lieber
1
test_bat_path = os.path.join(path, "test.bat")
Wenn Deine Python-Umgebung aktuell genug ist, kannst Du auch die Klasse
Path aus dem Standardmodul pathlib verwenden. Instanzen dieser Klasse
kannst Du einfach mit "+" konkatenieren, das kümmert sich automatisch um
den korrekten Pfadtrenner.
Weiterhin weiß ich ja nicht, wie das in Microsofts cmd.exe oder
Powershell ist, aber mit dem "&" würdest Du den Prozeß in UNIXoiden
Systemen sofort in den Hintergrund schicken. Das heißt, Deine "test.bat"
würde im Hintergrund laufen und noch gar kein Ergebnis (und keine
Umgebungsvariable "errorlevel" gesetzt haben), wenn Du Dein "echo
%errorlevel%" aufrufst. Dasselbe gilt für Dein "call set".
Allerdings brauchst Du den ganzen Zinnober über temporäre Dateien nicht,
wenn ich das richtig sehe. Du kannst das gesamte Environment als
Dictionary von str() in os.environ oder als Dictionary von bates() in
os.environb finden, und auch mit os.environ.get('errorlevel') die
Umgebungsvariable "%errorlevel%" lesen.
Nur_ein_Typ schrieb:> Weiterhin weiß ich ja nicht, wie das in Microsofts cmd.exe oder> Powershell ist, aber mit dem "&" würdest Du den Prozeß in UNIXoiden> Systemen sofort in den Hintergrund schicken. Das heißt, Deine "test.bat"> würde im Hintergrund laufen und noch gar kein Ergebnis (und keine> Umgebungsvariable "errorlevel" gesetzt haben), wenn Du Dein "echo> %errorlevel%" aufrufst. Dasselbe gilt für Dein "call set".
du bist der 2. der das hier schreibt - unter windows cmd ist & das ; in
bash
Nur_ein_Typ schrieb:> Allerdings brauchst Du den ganzen Zinnober über temporäre Dateien nicht,> wenn ich das richtig sehe. Du kannst das gesamte Environment als> Dictionary von str() in os.environ oder als Dictionary von bates() in> os.environb finden, und auch mit os.environ.get('errorlevel') die> Umgebungsvariable "%errorlevel%" lesen.
das Environemnt wird von der ausgeführt Batch-Datei erst erzeugt -
dieses Environment will ich - das kann man nicht davor und auch nicht
danach einfach so abfragen - auch nicht unter linux - das Environment
gilt nur zum Ausführungszeitpunkt des cmd, und der error-level wird erst
in den callenden Process eingefügt - an den komme ich hier aber auch
nicht
d.h. deine Tips funktionieren einfach nicht
Nur_ein_Typ schrieb:> Statt "+" zur Konkatenation Deiner Pfade solltest Du die Pfadtrenner> lieber weglassen und die Funktion os.path.join benutzen. Also statt> 'test_bat_path=path+"\\test.bat"' lieber> test_bat_path = os.path.join(path, "test.bat")
das mache ich normalerweise schon - ich wollte mein Beispiel nur
"einfach" halten
Nur_ein_Typ schrieb:> Christian U. schrieb:>> In https://docs.python.org/3/library/os.html>> steht bei os.environ "This mapping is captured the first time the os>> module is imported, typically during Python startup>> Oh, stimmt, das hatte ich ganz vergessen... :-(
gibt es einen weg das wirklich aktuelle komplette Environment in Python
zu lesen - am besten als Dict?
oerks schrieb:>> call %1 %2 %3 %4 %5 %6 %7 %8 %9>> Das versagt doch schon bei Leerzeichen in den Parametern oder
aber einfaches Quoting würde es doch auch schon lösen
call "%1" "%2" "%3" "%4" "%5" "%6" "%7" "%8" "%9"...
oder meinst du was wilderes?
> mehr als neun Argumenten.
dann mache ich 64 dann reicht es mir bis 2031
diese call-datei gibt es ja nur 1 mal - who-cares wie lang die wird :)
> Beitrag "Win7x64/DOS/CMD-Batch: %errorlevel% kommt "manchmal" richtig raus?">> irgendwie ist das nicht so 100% deterministisch>> und in Python mit popen bekomme ich auch diese Effekte - also erst mal> unter pure CMD verstehen warum das so ist - vielleicht löst sich dann> mein Python-Problem automatisch mit
Tja, der dort angefügte Link https://ss64.com/nt/errorlevel.html
beschreibt ja unter Qellenangabe "MSFT" die "Mikrosoftschen Gründe":
Inconsistent Not consistent do vary / .bat != .cmd / ERRORLEVEL !=
%ERRORLEVEL% != ExitCode / vary by applied ServicePacks
Lösungsleitsatz: "Viel Glück, Du wirst es LEIDER brauchen..."
da gibs libs für schrieb:> Lösungsleitsatz: "Viel Glück, Du wirst es LEIDER brauchen..."
Sebastians Lösung mit der wrapper bat funktioniert (auch wenn ich dis
Hoffnung hatte auf eine zusätzliche bat Datei verzichten zu können)
besser als alles andere was ich probiert habe
Ich generiere jetzt diesen call wrapper direkt in Python und umgehe
damit alle Probleme (Quoting, Parameteranzahl, Abgreifen Errorlevel, und
des gesetztes Environments) und es läuft super, jetzt kann ich einen
Haufe alt Batches die mit allen Tricks if,for,awk,... arbeiten anfangen
durch trivialen Python code zu ersetzen :)
da gibs libs für schrieb:> Tja, der dort angefügte Link https://ss64.com/nt/errorlevel.html> beschreibt ja unter Qellenangabe "MSFT" die "Mikrosoftschen Gründe":> Inconsistent Not consistent do vary / .bat != .cmd / ERRORLEVEL !=> %ERRORLEVEL% != ExitCode / vary by applied ServicePacks
Das erklärt vieles, Danke