Script unter Windows wirft Fehler aus UnicodeDecodeError

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
SaWo82
User
Beiträge: 4
Registriert: Donnerstag 21. Oktober 2021, 13:15

Hallo,

ich hoffe das ich hier jetzt auch richtig bin. Ich bin was programmieren angeht der absolute noob. Aber ich habe mir ein Projekt überlegt an dem ich jetzt mühsam einiges geschafft habe. An dem ich jetzt aber hänge und trotz google und auch hier suche ich keine lösung finde. Habe mir auch das unicode paper von python angeschaut aber ich komm einfach nicht weiter.

Bei meinem Script handelt es sich um eine art Pingtool.

Ich lese eine txt Datei ein in der ein Host und eine IP steht und pinge diese IP und poste das Ergebnis dann in eine weitere txt Datei. Damit ich später mir aus den gesammelten Daten eine Grafische Ausgabe machen kann.

Mein Script läuft unter Mac OS wunderbar nur auf Windows nicht. Hier brauche ich jetzt hilfe.

Hier mein Code:

Code: Alles auswählen

import sys
from time import sleep
import os
import time

class color:
   PURPLE = '\033[95m'
   CYAN = '\033[96m'
   DARKCYAN = '\033[36m'
   BLUE = '\033[94m'
   GREEN = '\033[92m'
   YELLOW = '\033[93m'
   RED = '\033[91m'
   BOLD = '\033[1m'
   UNDERLINE = '\033[4m'
   END = '\033[0m'

# Erkenne das Betriebssystem nt = Windows, posix = Linux/Unix & Mac

scanos = os.name

timestamp = time.strftime('%d.%m.%Y %H:%M:%S', time.localtime())  # Zeitstempel erzeugen

with open("ip_list.txt") as file:
    dump = file.read()
    dump = dump.splitlines()
    print(dump)

# Ping each ip address in the list and write the result to output.txt
def ping_plotter_posix(f):
    #timestamp = time.strftime('%d.%m.%Y %H:%M:%S', time.localtime())  # Zeitstempel erzeugen

    for i in dump:

         # hier wird die ip_lis.txt aufgesplittet in ip und host und daraus wird dann der pingbefehl gebaut
         ip = i.split()[1]
         host = i.split()[0]
         #print(host)
         #print(ip)
         res = os.popen(f"ping {ip} -c 5").read() # pingbefehl wird auf der konsole ausgeführt

         # if anweisung zum suchen eine bestimmten ergebnisses, hier 64 bytes
         if "64 bytes" in res:
             #print(res)
             lines = res.splitlines()
             resultline = lines[9]
             part = resultline.split("/")
             avg = part[4]
             #print(avg)

             # hier splitte ich das ergebnis nach , auf um die Porzentzahl für den packetloss zu ermitteln
             resultline1 = lines[8]
             part1 = resultline1.split(",")
             ploss = part1[2]
             #print(ploss)

             f.write(str(host) + '\t' + str(timestamp) + '\t' + str(ip) + '\t' + 'UP' '\t' + 'avg ' + avg + '\t' + ploss + '\n')
             #f.close()
         else:
             # print(res)
             f.write(str(host) + '\t' + str(timestamp) + '\t' + str(ip) + '\t' + 'DOWN  X X X X X' + '\n')



def ping_plotter_nt(f):
    # timestamp = time.strftime('%d.%m.%Y %H:%M:%S', time.localtime())  # Zeitstempel erzeugen

    for i in dump:

        # hier wird die ip_lis.txt aufgesplittet in ip und host und daraus wird dann der pingbefehl gebaut
        ip = i.split()[1]
        host = i.split()[0]
        # print(host)
        # print(ip)
        res = os.popen(f"ping {ip} -n 5").read()  # pingbefehl wird auf der konsole ausgeführt

        # if anweisung zum suchen eine bestimmten ergebnisses, hier 64 bytes
        if "32 bytes" in res:
            # print(res)
            lines = res.splitlines()
            resultline = lines[11]
            part = resultline.split("=")
            avg = part[3]
            print(avg)

            # hier splitte ich das ergebnis nach , auf um die Porzentzahl für den packetloss zu ermitteln
            resultline1 = lines[9]
            part1 = resultline1.split(",")
            ploss = part1[0]
            print(ploss)

            f.write(str(host) + '\t' + str(timestamp) + '\t' + str(
                ip) + '\t' + 'UP' '\t' + 'avg ' + avg + '\t' + ploss + '\n')
            # f.close()
        else:
            print(res)
            f.write(str(host) + '\t' + str(timestamp) + '\t' + str(ip) + '\t' + 'DOWN  X X X X X' + '\n')


# loop for function ping_plotter, range = loopcount + sleep timer
try:
    print("Press Ctrl-C to terminate")

    if 'posix' in scanos:
        f = open("output.txt", "a")

        for loop in range(3):
            ping_plotter_posix(f)
            sleep(1)
        f.close()
    elif 'nt' in scanos:
        f = open("output.txt", "a")

        for loop in range(3):
            ping_plotter_nt(f)
            sleep(1)
        f.close()

    else:
        print('Das Betriebssystem ' + color.BOLD + color.RED + scanos + color.END + ' ist mir nicht bekannt.')
        sys.exit()

except KeyboardInterrupt:
    print("Press Ctrl-C to terminate")

    pass



# Open output.txt file
with open("output.txt") as file:
    output = file.read()

print(output)
Wie Ihr sehen könnt ist da noch viel Verbesserungswürdig. Aber es geht mir auch nur mal darum die Funktion zum laufen zu bringen.

Wenn ich jetzt dieses Script auf Windows ausführe dann bekomme ich Folgende Fehlermeldung.

Traceback (most recent call last):
File "C:\Users\sascha\Documents\pyscript\main.py", line 117, in <module>
ping_plotter_nt(f)
File "C:\Users\XXX\Documents\pyscript\main.py", line 77, in ping_plotter_nt
res = os.popen(f"ping {ip} -n 5").read() # pingbefehl wird auf der konsole ausgeführt
File "C:\Users\XXX\AppData\Local\Programs\Python\Python310\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 18: character maps to <undefined>


Ich habe selbst jetzt mehrere Codecs ausprobiert aber leider ohne erfolg.

Kann mich jemand mit der Nase auf die Lösung stoßen?
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@SaWo82: Anmerkungen zum Quelltext: Es ist nicht konsistent eingerückt. Konvention sind vier Leerzeichen pro Ebene.

Klassennamen werden in PascalCase geschrieben, also `Color` statt `color`.

Ob die ANSI-Escape-Codes ohne weiteres unter Windows funktionieren, hängt von den Systemeinstellungen ab. Man nimmt da üblicherweise ein Package wie `colorama` mit an Bord, damit das plattformübergreifend funktioniert.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Was ist der Sinn dahinter `os.name` an den Namen `scanos` zu binden?

Bei `time.strftime()` ist die lokale Zeit der Default-Wert.

Der Kommentar ``# Erkenne das Betriebssystem nt = Windows, posix = Linux/Unix & Mac`` steht Meilenweit von Code entfernt der das dann auch tatsächlich tut. Es sind grundsätzlich zu viele Kommentare da drin die offensichtliches aussagen und somit keinerlei Nutzen haben.

Was soll der Kommentar ``# loop for function ping_plotter, range = loopcount + sleep timer``? Da ist gar keine Schleife wo der Kommentar steht.

Die Aufbereitung der eingelesenen Daten sollte schon beim einlesen passieren, und nicht später zweimal identisch in Funktionen stehen. Die Funktionen bekommen die (IP, Host)-Paare und sollten nicht wissen müssen *woher* die kommen. `dump` ist auch kein guter Name.

``in`` ist der falsche Operator wenn man zwei Zeichenketten auf *Gleichheit* testen möchte. Insbesondere so kurze Sachen wie "nt" könnte ja *in* allen möglichen Zeichenketten vorkommen.

Im ``if`` und im ``elif`` steht nahezu der gleiche Code. Man kopiert keinen Code und passt den leicht an. Entscheide dort nur, was sich wirklich unterscheidet, nämlich welche Ping-Funktion verwendet werden soll. Der Rest kann dann *einmal* nach dem ``if``/``elif``/``else``-Konstrukt stehen.

Für die Zuordnung von `os.name` zu der passenden Funktion kann man statt ``if``/``elif`` auch ein Wörterbuch verwenden.

Auch beim öffnen der Datei zum Anhängen sollte man ``with`` verwenden. Und `f` ist kein guter Name. Wenigstens für `file` sollte es reichen.

Das zusammenstückeln von Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. Dafür gibt es die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale.

``pass`` macht grundsätzlich nur Sinn, wenn das die einzige Anweisung in einem Block ist.

Die beiden `ping_plotter*()`-Funktionen brauchen neben der Ausgabedatei auch die IPs und Hosts und den Zeitstempel als Argument. Wobei *der* wohl besser frisch für jeden Eintrag in der Datei erzeugt werden sollte, denn die liegen ja mindestens eine Sekunde auseinander.

Die beiden Funktionen enthalten extrem viel gleichen Code und Kommentare, das scheint auch wieder eine Kopieraktion gewesen zu sein. Nicht machen! Da sollte man die Unterschiede herausziehen. In diesem Fall würde sich eine Klasse anbieten.

Anstelle von `os.popen()` sollte man das `subprocess`-Modul verwenden um externe Prozesse auszuführen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import os
import subprocess
import time
from collections import namedtuple
from functools import partial
from time import sleep


class Color:
    BLUE = "\033[94m"
    CYAN = "\033[96m"
    DARKCYAN = "\033[36m"
    GREEN = "\033[92m"
    PURPLE = "\033[95m"
    RED = "\033[91m"
    YELLOW = "\033[93m"

    BOLD = "\033[1m"
    END = "\033[0m"
    UNDERLINE = "\033[4m"


def _ping(repetition_option, ip, repetitions=5):
    return subprocess.run(
        ["ping", repetition_option, str(repetitions), ip],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        check=True,
    ).stdout


posix_ping = partial(_ping, "-c")
nt_ping = partial(_ping, "-n")


ValueLocator = namedtuple("ValueLocator", "line_index delimiter part_index")


def _parse_ping_output(success_text, value_locators, text):
    if success_text in text:
        lines = text.splitlines()
        return [
            lines[locator.line_index].split(
                locator.delimiter, locator.part_index + 1
            )[locator.part_index]
            for locator in value_locators
        ]
    else:
        return None


parse_posix_ping_output = partial(
    _parse_ping_output,
    "64 bytes",
    [ValueLocator(9, "/", 4), ValueLocator(8, ",", 2)],
)
parse_nt_ping_output = partial(
    _parse_ping_output,
    "32 bytes",
    [ValueLocator(11, "=", 3), ValueLocator(9, ",", 0)],
)


def ping_plotter(ping_function, parse_function, file, hosts_and_ips):
    for host, ip in hosts_and_ips:
        result = parse_function(ping_function(ip))
        if result is None:
            state = "DOWN  X X X X X"
        else:
            average, packet_loss = result
            state = f"UP\tavg {average}\t{packet_loss}"

        timestamp = time.strftime("%d.%m.%Y %H:%M:%S")
        file.write(f"{host}\t{timestamp}\t{ip}\t{state}\n")


def main():
    try:
        with open("ip_list.txt", encoding="ascii") as lines:
            hosts_and_ips = [line.strip().split() for line in lines]

        print(hosts_and_ips)
        print("Press Ctrl-C to terminate")

        functions = {
            "posix": (posix_ping, parse_posix_ping_output),
            "nt": (nt_ping, parse_nt_ping_output),
        }.get(os.name)

        if functions:
            ping_function, parse_function = functions
            with open("output.txt", "a", encoding="utf-8") as file:
                for _ in range(3):
                    ping_plotter(
                        ping_function, parse_function, file, hosts_and_ips
                    )
                    sleep(1)

            with open("output.txt", encoding="utf-8") as file:
                print(file.read())
        else:
            print(
                f"Das Betriebssystem"
                f" {Color.BOLD}{Color.RED}{os.name}{Color.END}"
                f" ist mir nicht bekannt."
            )

    except KeyboardInterrupt:
        print("Bye...")


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
SaWo82
User
Beiträge: 4
Registriert: Donnerstag 21. Oktober 2021, 13:15

Hallo __blackjack__,

Vielen Dank für deine Konstruktive Kritik. Ich muss zugeben ich bin etwas niedergeschlagen...

Meine Motivation mit dem Programmieren anzufangen war die Geburt meines Sohnes und ein kleiner Teil gewisse Aufgaben meiner Arbeit zu Automatisieren. Ich versuche mir gerade python selber beizubringen und da ist natürlich solche Kritik willkommen. Auch wenn sie ganz schön weh tut.
Zugegeben ich war schon Stolz als ich meinen ersten Ping absenden konnte via Script. Noch stolzer war ich als ich mehrere Geräte nacheinander anpingen konnte. Auf meinem Mac hat eben alles funktioniert und Ziel war es das eben auch das Script auf Windows läuft, wo ich dann eben auf probleme stieß...

Ich werde jetzt auf deine Anmerkungen Antworten:

__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 @SaWo82: Anmerkungen zum Quelltext: Es ist nicht konsistent eingerückt. Konvention sind vier Leerzeichen pro Ebene.
Wusste ich nicht. Danke
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Klassennamen werden in PascalCase geschrieben, also `Color` statt `color`.
Das Stimmt hatte ich irgendwo auch gelesen gesehen. Habs wohl vergessen.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Ob die ANSI-Escape-Codes ohne weiteres unter Windows funktionieren, hängt von den Systemeinstellungen ab. Man nimmt da üblicherweise ein Package wie `colorama` mit an Bord, damit das plattformübergreifend funktioniert.
Danke da werde ich reinlesen.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Was ist der Sinn dahinter `os.name` an den Namen `scanos` zu binden?
Ok habe ich so nicht gewusst.

Der Sinn hinter `scanos` ist eine variable zu haben wo die Information des betriebsystems steht. Damit mein Programm weiß welche Ping ausgeführt und welche Daten kollekted werden.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Bei `time.strftime()` ist die lokale Zeit der Default-Wert.
Ich habs leider nur so hinbekommen wie ich es hinbekommen habe.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Der Kommentar ``# Erkenne das Betriebssystem nt = Windows, posix = Linux/Unix & Mac`` steht Meilenweit von Code entfernt der das dann auch tatsächlich tut. Es sind grundsätzlich zu viele Kommentare da drin die offensichtliches aussagen und somit keinerlei Nutzen haben.

Was soll der Kommentar ``# loop for function ping_plotter, range = loopcount + sleep timer``? Da ist gar keine Schleife wo der Kommentar steht.
Oh, das mit den Kommentaren habe ich so gemacht um zu wissen was ich da angestellt habe. Mir ist klar das diese eher bescheiden sind. Mir sollte es helfen nach lägerer Pause wieder zu verstehen was ich da gemacht habe. Mir ist durchaus Bewusst das ich da nacharbeiten muss.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Die Aufbereitung der eingelesenen Daten sollte schon beim einlesen passieren, und nicht später zweimal identisch in Funktionen stehen. Die Funktionen bekommen die (IP, Host)-Paare und sollten nicht wissen müssen *woher* die kommen. `dump` ist auch kein guter Name.

``in`` ist der falsche Operator wenn man zwei Zeichenketten auf *Gleichheit* testen möchte. Insbesondere so kurze Sachen wie "nt" könnte ja *in* allen möglichen Zeichenketten vorkommen.
Habe ich einfach nicht besser gewusst. Wieder was gelernt.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Im ``if`` und im ``elif`` steht nahezu der gleiche Code. Man kopiert keinen Code und passt den leicht an. Entscheide dort nur, was sich wirklich unterscheidet, nämlich welche Ping-Funktion verwendet werden soll. Der Rest kann dann *einmal* nach dem ``if``/``elif``/``else``-Konstrukt stehen.
Da habe ich wohl noch Wissens Nachholbedarf.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Für die Zuordnung von `os.name` zu der passenden Funktion kann man statt ``if``/``elif`` auch ein Wörterbuch verwenden.
Ok... Der Kopf fängt an zu schwirren....
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Auch beim öffnen der Datei zum Anhängen sollte man ``with`` verwenden. Und `f` ist kein guter Name. Wenigstens für `file` sollte es reichen.
Ich hatte das so in viel Beispielcode im Netz gesehen... Dachte das wäre normal. Mit file wäre das mir lieber dann ist es ja verständlicher.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Das zusammenstückeln von Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. Dafür gibt es die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale.
Ich wusste es nicht besser.

__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 ``pass`` macht grundsätzlich nur Sinn, wenn das die einzige Anweisung in einem Block ist.
Auch da habe ich mich auf das Internet verlassen. Und habe mir mit meinem gefährlichen Halbwissen das zusammengestückelt.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Die beiden `ping_plotter*()`-Funktionen brauchen neben der Ausgabedatei auch die IPs und Hosts und den Zeitstempel als Argument. Wobei *der* wohl besser frisch für jeden Eintrag in der Datei erzeugt werden sollte, denn die liegen ja mindestens eine Sekunde auseinander.
Sollte man meinen ja. Aber ich möchte später mir ein Diagramm Anzeigen lassen wie die avg time des Pings war und das sollte wegen der übersicht immer die gleiche zeit haben auch wenn die pings hintereinander abgesetzt werden. Zumindest mein Idee dahinter.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Die beiden Funktionen enthalten extrem viel gleichen Code und Kommentare, das scheint auch wieder eine Kopieraktion gewesen zu sein. Nicht machen! Da sollte man die Unterschiede herausziehen. In diesem Fall würde sich eine Klasse anbieten.
Ich habe noch nicht das Verständniss aufgebracht wie Klassen genau funktionieren.
__blackjack__ hat geschrieben: Donnerstag 21. Oktober 2021, 17:03 Anstelle von `os.popen()` sollte man das `subprocess`-Modul verwenden um externe Prozesse auszuführen.
ok....


Nochmal vielen Dank für deine Mühe. Der Einstieg in das Programmieren ist verdammt schwer. Vielleicht habe ich mir auch das falsche projekt ausgesucht. Und vielleicht währe es besser einen Kurs zu besuchen.
Ich werde mir dein Code ansehen und versuchen zu verstehen.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Vielleicht habe ich mir auch das falsche projekt ausgesucht. Und vielleicht währe es besser einen Kurs zu besuchen.
Nee, das ist schon richtig, wie du es machst. Genereller Konsens hier im Forum ist immer: man lernt als Anfänger i.d.R. am meisten, wenn man ein Projekt umsetzt, an dem man _persönliches_ Interesse (bzw. einen Vorteil) hat, weil dann eine konkrete Motivation vorliegt. Bei Kursen, mit irgendwelchen generischen Aufgabenstellungen, von denen man eigentlich nichts hat, ist das mit der Motivation schwieriger.

Des Weiteren ist es bei den ersten Projekten, dass man die mindesten 1x komplett neu schreibt - was auch nicht weiter schlimm ist. Learning by doing :-)

Gruß, noisefloor
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich kann noisefloor da nur beipflichten. In beiden Punkten. Selbst als jahrzehntelanger Profi wirft man andauernd Code weg. Es ist ein Kardinalfehler, dem zu sehr anzuhängen. Code ist weniger wert, als man gemeinhin denkt. Die Lektionen, die man gelernt hat, sind der eigentliche Wert.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Dann möchte ich auch noch mal bekräftigen was meine Vorredner geschrieben haben: Code zu überarbeiten ist normal. Und dabei schreibt man auch mal etwas so stark um, dass es im Grunde ganz anders aussieht als vorher.

Vielleicht weil eine neue Version von Python ein neues Sprachfeature hat, oder ein neues Modul/eine neue Funktion, und man diese Neuerung sinnvoll einsetzen kann.

Und oft auch weil sich Code weiterentwickelt. Funktionen/Methoden werden zu gross und werden dann aufgeteilt. Man schreibt neuen Code und entdeckt ein Muster das bereits im vorhandenen Code steht — im deutlichsten Fall weil man Code kopiert und anpasst, was man eher nicht machen sollte. Und dann überlegt man sich, wie man diese Wiederholung oder das Muster aus dem vorhandenen Code heraus ziehen kann. Oft durch eine Funktion, welche die Unterschiede als Argumente bekommt, oder durch eine Schleife wo die Unterschiede in eine Datenstruktur heraus gezogen werden. Oder eine Kombination aus beidem.

Bei Deinem Projekt sieht man ja recht deutlich, dass die beiden Funktionen für Posix und NT Kopien sind, die jeweils auf das Betriebssystem angepasst sind. Sich aber letztlich hauptsächlich nur durch den Optionsnamen beim ``ping`` und den Positionen der Interessanten Werte in der Ausgabe unterscheiden. Das kann man dann mit *einer* Funktion lösen, welche diese unterschiede als Argumente übergeben bekommt.

Die Frage bei `scanos` ist halt warum Du `os.name` damit einen anderen Namen verpasst, denn das ist ja schon das was Du brauchst. `os.name` ist nicht so wirklich viel mehr zu tippen, und `scanos` ist IMHO auch kein verständlicherer Name. Weil das so die beiden Hauptmotivationen sind einen Wert den es schon unter einem Namen gibt, an einen anderen zu binden.

Bezüglich `strftime()`: Wenn man das zweite Argument weg lässt, dann nimmt die Funktion per Default die aktuelle lokale Zeit:

Code: Alles auswählen

In [216]: time.strftime("%d.%m.%Y %H:%M:%S", time.localtime())                  
Out[216]: '22.10.2021 13:15:39'

In [217]: time.strftime("%d.%m.%Y %H:%M:%S")                                    
Out[217]: '22.10.2021 13:15:40'
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
DeaD_EyE
User
Beiträge: 1017
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

SaWo82 hat geschrieben: Freitag 22. Oktober 2021, 09:04Ich versuche mir gerade python selber beizubringen und da ist natürlich solche Kritik willkommen. Auch wenn sie ganz schön weh tut.
Das Gefühl haben sogar erfahrene Entwickler, wenn man ihnen ihre Fehler aufzeigt.
Zugegeben ich war schon Stolz als ich meinen ersten Ping absenden konnte via Script. Noch stolzer war ich als ich mehrere Geräte nacheinander anpingen konnte.
Auch dieses positive Gefühl geht niemals weg.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Buchfink
User
Beiträge: 193
Registriert: Samstag 11. September 2021, 10:16

Das Gefühl haben sogar erfahrene Entwickler, wenn man ihnen ihre Fehler aufzeigt.
Bei mir auf der Arbeit sprechen die Entwickler von "ihrem Baby" wenn es um "ihren" Code geht.
Man ahnt irgendwie, warum. :)
SaWo82
User
Beiträge: 4
Registriert: Donnerstag 21. Oktober 2021, 13:15

Hallo zusammen,

Ich tue was man wohl so tut. Nochmal alles nach meinem wissen und gelernten und hier gezeigten neu schreiben. Ich habe aber einen hänger. Wieder mal mit Windoof. :(
Übrigens konnte ich das codec Problem mit dem richtigen enconding entschärfen. Es liegt an den umlauten in der Ausgabe vom deutschen Betriebssystem. Aber jetzt habe ich ein weiteres Problem.

Hier nochmal die Fakten:

Ich habe zwei txt Dateien.
1) Host zu IP liste mit aktuell zwei einträgen. 'google-DNS 8.8.8.8' und 'cloudflare 1.1.1.1'
2) eine output Datei in die ich die Konsolenausgabe schreibe plus timestamp und Host.

Wenn ich jetzt das Programm unter Windows starte dann schreibt er mir die ersten Ping ergebnisse von der Ip Host Liste in die output Datei. Aber die Ergebnisse des zweiten hosts nicht. Es kommt folgender Fehler:

Traceback (most recent call last):
File "C:\Users\sascha\Documents\pyscript\main.py", line 48, in <module>
ping_results = [line.strip().split() for line in lines]
ValueError: I/O operation on closed file.


zeile 48 ist:

ping_results = [line.strip().split() for line in lines]


Das ist mein Code:

Code: Alles auswählen

import subprocess
import time


with open("ip_list.txt", encoding="iso-8859-1") as lines:
    hosts_and_ips = [line.strip().split() for line in lines]
    print(hosts_and_ips)


timestamp = time.strftime("%d.%m.%Y %H:%M")


for host, ip in hosts_and_ips:
    ping = subprocess.run(['ping', (ip) ], encoding="iso-8859-1", capture_output=True, text=True)
    #print(host)
    print(host,'\n',
          ping.stdout)
    file = open("output.txt", "a", encoding="iso-8859-1")
    file.write(f"{host}:\t{timestamp}\n\n{ping.stdout}\n\n")
    ping_results = [line.strip().split() for line in lines]
    print(ping_results)
    file.close()
Ich freue mich auf eure Anregungen und natürlich verbesserungsvorschlägen. :)

grüße
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@SaWo82: Die Meldung ist doch recht eindeutig. Und die einzige Datei in der Zeile wo die Ausnahme auftritt ist `lines`, und die Datei ist zu dem Zeitpunkt halt geschlossen. Es macht auch eher keinen Sinn die da noch mal lesen zu wollen, denn die enthält ja auch gar nichts was zu dem Namen `ping_result` passen würde. Kann es sein, dass Du da eigentlich `ping.stdout` in Zeilen zerlegen wolltest?

Bei der zweiten Datei sollte man auch ``with`` verwenden.

Code: Alles auswählen

#!/usr/bin/env python3
import subprocess
import time


def main():
    with open("ip_list.txt", encoding="iso-8859-1") as lines:
        hosts_and_ips = [line.strip().split() for line in lines]
        print(hosts_and_ips)

    timestamp = time.strftime("%d.%m.%Y %H:%M")
    for host, ip in hosts_and_ips:
        ping = subprocess.run(
            ["ping", ip],
            encoding="iso-8859-1",
            capture_output=True,
            text=True,
            check=True,
        )
        # print(host)
        print(host, "\n", ping.stdout)
        with open("output.txt", "a", encoding="iso-8859-1") as file:
            file.write(f"{host}:\t{timestamp}\n\n{ping.stdout}\n\n")
        #
        # XXX ???
        #
        ping_results = [
            line.strip().split() for line in ping.stdout.splitlines()
        ]
        print(ping_results)


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du öffnest zweimal eine Datei. Einmal nennst du den resultierenden Stream "lines" und beim zweiten Mal "file". Da greifst du aber wieder auf "lines" zu, was oben schon längst wieder geschlossen wurde. Nur der Name existiert halt noch.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du greifst nochmal auf lines zu, das du oben als Datei geoeffnet hast. Und dank dem with-Statement (und korrekterweise) auch wieder geschlossen hast. Ich weiss nicht auf was du stattdessen zugreifen willst, aber der Name "ping_results" suggeriert, das haette etwas mit ping zu tun.

Der Fehler waere zu vermeiden durch bessere Namen (lines ist generisch. ip_list_txt als Dateiname waere zB etwas, bei dem dir der Fehler ggf. selbst auffiele), und Kapselung in Funktionen.
SaWo82
User
Beiträge: 4
Registriert: Donnerstag 21. Oktober 2021, 13:15

:oops: ich bin ein vollhorst. :( das soll da garnicht rein. Sowas von tomaten auf den Augen gehabt. Ich wollte ein test machen wie ich die Daten nachher weiterverarbeiten kann. Zum Beispiel will ich das wenn der Ping erfolgreich war das untereinander ausgegeben wird Host IP ist Up oder eben DOWN.
Antworten