Subprocess Problem(Längere Ausgabe im Terminal anzeigen lassen)

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
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Hallo.
Ich hänge jetzt seit einer Woche an ein Problem für das ich keine Lösung finde.
Ich möchte für mich selber ein Backup Programm erstellen, dass dann einen rsync Befehl ausführt (genauer: ein Backup ausführt).

Das was ich nicht zusammenbringe:
Ich möchte den über "subprocess" ausgeführten "rsync" Befehl im Termnial anzeigen lassen und alles was da angezeigt wurde in eine Datei speichern (für eventuell spätere Fehlersuche).

In einem anderen Forum wurde mir logging empfohlen, aber da schaffe ich es immer nur dass mir die erste Zeile angezeigt wird, oder alles am Schluss.
Bei einem Backup dass vielleicht eine Stunde oder mehr läuft würde ich gerne sehen, dass noch was passiert, bzw. anhand der Dateien die gerade kopiert werden sehen wollen wie weit es ungefähr ist.

Hat von euch jemand eine Idee wie man die Ausgabe eines Subprocesses in Echtzeit im Terminal anzeigen lassen kann? Glaub das mit dem in der Datei speichern bekomm ich dann wieder selber hin.

Vielen Dank im Voraus

Gruß Thomas
Benutzeravatar
Dennis89
User
Beiträge: 1518
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

wie hast du das mit der Log-File denn probiert?
'rsync' bietet von Haus aus Log-Option an:
https://wiki.ubuntuusers.de/rsync/

Code: Alles auswählen

run([
    'rsync', '-vahHA', f'--log-file=/media/Data_Backup/Logs/{date.today()}.log',
    f'--link-dest=/media/Data_Backup/{date.today()}', '/media/Wolke/',
    f'/media/Data_Backup/{date.today()}'],
    check=True)
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Hi.
Danke für die Antwort.
Da hab ich mich wohl falsch ausgedrückt. Das mit dem Log-File ist nicht mein Problem, sondern die Anzeige des rsync-Verlaufs im Terminal.
Ich erhoffe mir davon, dass bei längeren Backups ein wenig ersichtlich ist wie weit das Backup schon ist, bzw. das ich abschätzen kann wie lange es noch ungefähr dauert.

Dafür wurde mir der import "logging" empfohlen (auf gutefrage.net), aber mit dem komm ich nicht so wirklich klar.

Auszüge meines Codes:
So definiere ich die Pfade (z.B.: um sie für weitere Backups schneller abändern zu können)

Code: Alles auswählen

# Pfade definieren
source = "/home/dns"  # -> Pfad welches gesichert werden soll definieren
target = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/"  # -> Pfad wohin gesichert werden soll
log_pfad = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/_Log-Files/"
backup_dir = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/_gelöschte_Dateien"
exclude = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/_rsync-Exclude-File/Rsync-Exclude-Linux-Home"
Backup Funktion:

Code: Alles auswählen

def backup_home(logfile=logfile_test()):
    # Liste von allem was an subprocess übergeben wird
    rsync_liste = ['rsync',
                   '-avPXAh',
                   ('--log-file=' + logfile),
                   '--delete',
                   ('--backup-dir=' + backup_dir),
                   ('--exclude-from=' + exclude),
                   source,
                   target]
                   
    # Backup-Subprocess:
    backup_process = subprocess.Popen(rsync_liste, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = backup_process.communicate()
Ist das möglich diesen Prozess im Terminal anzeigen zu lassen, als würde ich rsync als bash in der Konsole ausführen?
Sirius3
User
Beiträge: 18253
Registriert: Sonntag 21. Oktober 2012, 17:20

Defaultargumente werden bei der Definition der Funktion festgelegt, da möchte man normalerweise keine Funktionsaufrufe haben. Konstanten werden komplett gross geschrieben.
Und wenn Du die Ausgabe des Programms im Terminal haben willst, dann darfst Du sie eben nicht umleiten!

Code: Alles auswählen

# Pfade definieren
SOURCE = "/home/dns"  # -> Pfad welches gesichert werden soll definieren
TARGET = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/"  # -> Pfad wohin gesichert werden soll
LOG_PATH = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/_Log-Files/"
BACKUP_DIR = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/_gelöschte_Dateien"
EXCLUDE = "/mnt/Laufwerke/10_Backup-Linux/01_Backup-home-dns/_rsync-Exclude-File/Rsync-Exclude-Linux-Home"

def backup_home(logfile=None):
    if logfile is None:
        logfile = logfile_test()
    # Liste von allem was an subprocess übergeben wird
    arguments = ['rsync',
                 '-avPXAh',
                 '--log-file, logfile,
                 '--delete',
                 '--backup-dir', BACKUP_DIR,
                 '--exclude-from', EXCLUDE,
                 SOURCE,
                 TARGET]
    subprocess.run(arguments)
Benutzeravatar
__blackjack__
User
Beiträge: 14002
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Medds: Du darfst halt nicht `communicate()` verwenden, sondern musst `stdout` und `stderr` von dem Prozess auslesen und ausgeben und noch irgend etwas anderes damit machen. Wobei man da überlegen sollte `stderr` einfach nach `stdout` umzuleiten. also `subprocess.STDOUT` statt `subprocess.STDERR`.

Falls Du sonst nichts weiter mit den Zeilen machen willst, dann siehe die Antwort von Sirius3. Das ist dann einfacher. Ich würde das dort dann noch durch das Argument ``check=True`` beim `run()`-Aufruf ergänzen, damit Fehler beim ``rsync`` nicht einfach ignoriert werden.

Grunddatentypen haben nichts in Namen zu suchen. Namen von Konstanten werden KOMPLETT_GROSS_GESCHRIEBEN.

Ungetestet:

Code: Alles auswählen

def backup_home(log_filename):
    backup_process = subprocess.Popen(
        [
            "rsync",
            "-avPXAh",
            "--log-file=" + log_filename,
            "--delete",
            "--backup-dir=" + BACKUP_DIR,
            "--exclude-from=" + EXCLUDE,
            SOURCE,
            TARGET,
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
    )
    for line in backup_process.stdout:
        print(line)
        #
        # TODO Do something else with line.
        #
    
    if backup_process.wait() != 0:
        raise RuntimeError(
            f"Backup endet with non-zero exit code {backup_process.returncode}"
        )
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Vielen Dank euch beiden.

Die Variante von __blackjack__ funktioniert super.
Und das beste daran. Ich verstehe es sogar noch.
Namen von Konstanten werden KOMPLETT_GROSS_GESCHRIEBEN
habt ihr mir vor geraumer Zeit schon mal gesagt.
Da war das halbe Jahr Python-Pause nicht förderlich.

Shame on me

Macht ja auch Sinn. Ich versuche es diesmal besser zu verinnerlichen.

Was meinst du mit:
Grunddatentypen haben nichts in Namen zu suchen.
Kannst du ein Beispiel geben was du damit meinst?

Eine weitere Frage hätte ich noch:

Ist das

Code: Alles auswählen

 run() 
das gleiche wie

Code: Alles auswählen

 Popen() 
, oder hat

Code: Alles auswählen

 run() 
irgendwelche Vorteile?

Nochmals vielen Dank für tolle Hilfe.
Benutzeravatar
__blackjack__
User
Beiträge: 14002
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Medds: Grunddatentypen sind die eingebauten Datentypen für die es (in der Regel) auch Syntax gibt um literale Werte in das Programm zu schreiben. Also `int`, `float`, `str`, `list`, `dict`, und so weiter. Das Problem damit das als Namensbestandteil zu verwenden, ist, dass man während der Programmentwicklung öfter man auf andere, spezialisiertere Typen wechselt, und dann überall die dann falschen, irreführenden Namen anpassen muss.

`run()` ist einfacher wenn man nur ein externes Programm ablaufen lassen will, ohne das man im eigenen Code Zugriff auf das `Popen`-Objekt braucht. Denn das verwendet `run()` intern im das externe Programm auszuführen.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Ok Danke.
rsync_liste war falsch weil da der Grunddatentyp Liste drinnen stand. Danke für die ausführliche Erkärung.

Das 'Popen()' gegen das 'run()' auszutauschen hat bei mir nicht funktioniert, aber egal, weil 'Popen()' funktioniert.
check=True hat auch nicht funktioniert, aber denke das war für das run() gedacht.
Wenn ich das richtig verstehe, ist das
if backup_process.wait() != 0:
raise RuntimeError(
f"Backup endet with non-zero exit code {backup_process.returncode}"
)
das equivalent zum 'check=True'?
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Ah eine letzte Frage hätte ich noch.
Wenn ich die jetzigen Konstanten in eine Funktion schreibe bei der ich Auswählen kann welche Pfade ich benutzten möchte, sind das dann keine Konstanten mehr, oder?
Weil so ist das Programm gedacht, dass es dann mal so wird, dass am Anfang auszuwählen ist mit beispielsweise 1, 2 oder 3 welches Backup ich ausführen möchte.
Versteh ich das richtig dass dann nicht mehr groß geschrieben werden sollte?
Benutzeravatar
__blackjack__
User
Beiträge: 14002
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Medds: Da würde ich ja eher Kommandozeilenargumente für her nehmen. Dann stellt sich aber auch die Frage warum das überhaupt in Python geschrieben ist, und nicht einfach ein Shell-Skript.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Medds
User
Beiträge: 27
Registriert: Samstag 17. Oktober 2020, 19:19

Letztendlich soll es mein zweites PyQt5 Programm werden. Aber zuerst soll es mal zuverlässig laufen bevor ich eine GUI daraus mache.
Antworten