Python Ausgabe externer Programme verwerten / Encoding Windows Shell

Blutschlumpf

Fleet Admiral
Registriert
März 2001
Beiträge
20.380
Hallo,

ich bin gerade darüber gestolpert, dass ich ein externes Programm aufrufen und die Ausgabe verwerten möchte.
Beispiel:
Python:
import subprocess
response = subprocess.check_output("ping google.com", shell=True)

response ist dann vom Typ binary, da kommt dann z.B: raus:
Code:
b'\r\nPing wird ausgef\x81hrt f\x81r google.com [142.250.185.174] mit 32 Bytes Daten:\r\nAntwort von 142.250.185.174: Bytes=32 Zeit=12ms TTL=60\r\nAntwort von 142.250.185.174: Bytes=32 Zeit=12ms TTL=60\r\nAntwort von 142.250.185.174: Bytes=32 Zeit=12ms TTL=60\r\nAntwort von 142.250.185.174: Bytes=32 Zeit=12ms TTL=60\r\n\r\nPing-Statistik f\x81r 142.250.185.174:\r\n    Pakete: Gesendet = 4, Empfangen = 4, Verloren = 0\r\n    (0% Verlust),\r\nCa. Zeitangaben in Millisek.:\r\n    Minimum = 12ms, Maximum = 12ms, Mittelwert = 12ms\r\n'

Das System auf dem ich das mache ist Windows 10 mit Python 3.9.9
sys.stdout.encoding -> "utf-8"
sys.stdin.encoding -> "utf-8"
sys.getfilesystemencoding() -> "utf-8"
sys.getdefaultencoding() -> "utf-8"
locale.getpreferredencoding() -> "cp1252"

response.decode('utf-8') endet mit nem Fehler:
Code:
Traceback (most recent call last):
  File "c:\git\pytest\testpad.py", line 10, in <module>
    print(response.decode('utf-8'))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x81 in position 18: invalid start byte

Der Encoding-Parameter im subprocess-Modul bricht auch mit nem Fehler (UnicodeDecodeError: 'utf-8' codec can't decode byte 0x81 in position 18: invalid start byte) ab:
subprocess.check_output("ping google.com", encoding="utf-8")

Bei cp1252 siehts ähnlich aus:
Code:
UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 18: character maps to <undefined>

Über Google bin ich drauf gestoßen, dass die Kodierung CodePage850 passen könnte.
response.decode('cp850') gibt (zumindest in dem Fall) auch das korrekte Ergebnis aus.

Aber wie komme ich da vom trial and error weg, sprich wo kann ich auslesen was die Shell (unabhängig vom OS bzw. dem verwendeten Rechner) da zurück gibt?
Mit Google habe ich nichts hilfreiches und meist auch nur uralte Antworten gefunden.
Auf nem Debian 11 ist alles incl. des Konsolen-Outputs UTF-8, da wäre das jetzt gar nicht aufgefallen.

Gibts nen besseren Weg externe Programme zu starten und encoding-Probleme komplett zu umgehen?

Der Ping-Befehl ist nur ein Beispiel, ein "dir" (Antwort enthält ein ä in Datenträger) hätte ähnliche Probleme.
 
chcp 65001 ändert das Encoding auf UTF-8. Das bekommst du sicherlich auch irgendwie in deinem Aufruf unter.
 
Dann ist aber Windows-spezifisch gebaut und ich weiß trotzdem nicht obs auf anderen Systemen nicht auch ähnliche Problem gibt.
Es muss doch irgendeine saubere Lösung geben, ein externes Programm aufzurufen dürfte ja keine totale Ausnahemanwendung darstellen sondern eher daily business sein.
 
Ich bin mir nicht sicher worauf du hinaus willst.
In welcher Variante tritt das Problem deines Wissens nach nicht auf?

errors="ignore" in der ignore-Funktion löst das Problem nicht, er löscht schlichtweg die Zeichen raus oder setzt da alternativ Platzhalter rein.
Verhindert natürlich den Fehler, aber verfälscht natürlich die Rückgabe.
 
Da gibts OS-unabhängig nichts (soweit ich weiß) und Zeichensatz-Analyse ist weiterhin nicht viel mehr als Glaskugeln, da haben sich schlauere Leute als wir schon die Zähne dran ausgebissen. 😭

chcp ist so ne Sache, das geht nur in cmd.exe. PS hätte wieder $InputEncoding und $OutputEncoding. Und Dateisysteme versagen ganz, auch unter Windows, weil da tatsächlich noch ANSI und Unicode-codierte Dateinamen drinstecken können. Unter Linux isses buchstäblich wumpe, für die sind Dateinamen binär und mit Basteln kann man sogar im selben Dateinamen verschiedene Encodings haben (okay, das wäre mutwillig).

Wenn es wirklich nur DE sein soll und da die Auswahl ist zwischen UTF8, ANSI und cp437/850, dann würde ich es persönlich brute-force mäßig einfach auf bekannte Muster testen. Bspw oben, wenn 0x81 enthalten ist im String, dann isses wohl cp850. (Nur zur Veranschaulichung, hab grad nicht auf dem Schirm, ob man 0x81 als Byte noch groß anderswo finden würde.)

Oder wie bereits angedeutet (aber anscheinend nicht zuende gedacht) daß man einfach die Konversion mit "Ignore Error" drüberlaufen läßt, "hofft" daß sich dadurch Input und Output unterscheiden, weil "Ignore" Teile des Inputs verwirft; und dann Input und Output vergleichen. Sind sie verschieden, war das wohl nicht mein Zeichensatz.

Klar, schön ist was anderes. Aber es sollte prinzipiell funktionieren.
 
Zurück
Oben