x3 226108f4dc Up-/Download gefixt: absoluter Remote-Pfad statt "/./datei" (#3)
paramikos getcwd() liefert None, solange kein chdir() erfolgt ist. Der
Rueckfall auf "." landete als remote_path in der App, woraus _join()/_abs()
Pfade wie "/./datei" bauten. Der Server loeste die relativ zum Dateisystem-
Root statt zum Home auf -> "No such file" beim Download, "Permission denied"
beim Upload. Das Listing funktionierte, weil listdir(".") serverseitig
relativ zum Home aufgeloest wird -- daher fiel es erst beim Transfer auf.

- connect()/chdir()/listdir() loesen den Pfad ueber sftp.normalize() auf
- _abs() haengt relative Pfade an das CWD an, statt blind "/" voranzustellen
- _copy_tree(): die beiden mkdir-Zweige waren vertauscht. Beim Download
  wurde backend.mkdir() auf einen lokalen Pfad angewendet (Verzeichnis
  entstand auf dem Server), beim Upload os.makedirs() auf einen entfernten.
  Ordner-Transfers waren dadurch in beide Richtungen defekt.

Verifiziert per End-to-End-Test gegen einen echten sshd/SFTP-Server:
Datei- und rekursiver Ordner-Transfer in beide Richtungen.

Closes #3
2026-08-31 17:19:15 +00:00

ftptui

Interaktiver FTP- und SFTP-Client als Terminal-User-Interface in Python.

ftptui ist ein schlanker, aber vollwertiger Dateibrowser für die Kommandozeile. Er unterstützt klassisches FTP (RFC 959, über die Python-Standardbibliothek ftplib) sowie SFTP (SSH File Transfer Protocol, über paramiko). Damit deckt das Tool den überwiegenden Teil der realen Dateiübertragungs-Szenarien ab – einschließlich SSH-basiertem FTP.

Das Interface ist zweispaltig aufgebaut:

  • links das lokale Dateisystem,
  • rechts das entfernte System,

mit Dateiübertragung in beide Richtungen, rekursivem Hoch-/Herunterladen von Verzeichnissen, Anlegen, Umbenennen und Löschen sowie speicherbaren Verbindungsprofilen.


Inhaltsverzeichnis


Funktionen

  • Dual-Pane-Dateibrowser – Layout und Bedienung im Stil von Total Commander / Midnight Commander: zwei benachbarte Panele (lokal & entfernt) mit den Spalten Name, Größe, Datum, einer Pfadleiste oben, einer Statusleiste unten und grün markiertem aktivem Panel.
  • Klassisches Terminal-Thema – schlanker Dark-Look angelehnt an den Dateimanager ranger: schwarzer Hintergrund, Verzeichnisse grün/fett, Auswahl per inverser (hellblauer) Markierung.
  • FTP über die Standardbibliothek, SFTP über paramiko (SSH-FTP).
  • Herunterladen (l) und Hochladen (u) einzelner Dateien.
  • Rekursive Übertragung kompletter Verzeichnisbäume.
  • Verzeichnisse anlegen (n), umbenennen (r), löschen (d), rekursives Löschen nicht-leerer Ordner.
  • Navigation mit Maus/Tastatur, „eine Ebene hoch“ (←/⌫), Enter öffnet.
  • Verbindungsprofile werden als JSON unter ~/.config/ftptui/profiles.json gespeichert und können wieder geladen werden.
  • Passwortfelder sind abgedunkelt (password=True).
  • Gebaut mit dem terminalfreundlichen TUI-Framework Textual, ohne fancy Material-Design – ein „echtes“ Konsolenprogramm.

Unterstützte Protokolle

Protokoll Beschreibung Bibliothek / Quelle
ftp Klassisches File Transfer Protocol (RFC 959) Standard (kein Drittpaket nötig)
sftp SSH File Transfer Protocol (SSH-FTP) paramiko

Hinweis: „SSH-FTP“ kann zweierlei bedeuten – das SFTP-Subsystem über eine SSH-Schicht (hier umgesetzt) oder FTP über einen SSH-Tunnel (sog. „FTP over SSH“). In ftptui wird SFTP über SSH unterstützt, der heute übliche und sichere Weg.


Installation

Voraussetzungen

  • Python ≥ 3.10
  • pip und idealerweise eine virtuelle Umgebung

Aus dem Quellverzeichnis

# virtuelle Umgebung anlegen und aktivieren
python3 -m venv .venv
source .venv/bin/activate

# Abhängigkeiten installieren
pip install -e .

# Abhängigkeiten (nur Laufzeit):
#   textual  - TUI-Framework
#   paramiko - SFTP/SSH

Installations- & Deinstallations-Skripte (systemweit)

Im Projektverzeichnis liegen zwei Scripts für eine systemweite Installation auf Debian/Ubuntu (PEP 668 „externally managed“ wird berücksichtigt: es wird eine eigene Umgebung unter /opt/ftptui/venv angelegt und nach /usr/local/bin/ftptui verlinkt):

./install.sh            # systemweit installieren (fragt ggf. nach sudo)
./install.sh --force    # erneut installieren/aktualisieren

./uninstall.sh          # systemweite Installation entfernen (Nutzdaten bleiben)
./uninstall.sh --purge  # zusätzlich Konfigurationsdateien löschen

Nach ./install.sh steht das Kommando ftptui global im PATH bereit.

Als Paket

pip install .

Als installiertes Kommando

Nach pip install -e . steht das Kommando ftptui global in der Umgebung bereit:

ftptui

Verwendung

  1. Starten: ftptui

  2. Im Verbindungsbildschirm die Daten eingeben:

    • Verbindungsprofil – ein zuvor gespeichertes Profil auswählen, oder ein neues anlegen.
    • Protokoll – sftp (SSH) oder ftp.
    • Host, Port (SFTP-Vorgabe 22, FTP-Vorgabe 21), Benutzer, Passwort.
    • Auf Verbinden klicken. Der Port wird bei Protokollwechsel automatisch auf den jeweiligen Standard gesetzt.
  3. Nach erfolgreicher Verbindung erscheint der Browser-Bildschirm mit zwei Panelen:

    • links / des lokalen Rechners bzw. das Home-Verzeichnis,
    • rechts das Home-/Root-Verzeichnis des entfernten Systems.
  4. Zwischen den Paneelen wechseln mit Tab. Die fokussierte Tabelle erhält die Aktionen (Hoch-/Herunterladen, Umbenennen, Löschen …).

Tipp: Die Aktionen u/l wirken auf das gerade in der fokussierten Tabelle angewählte Element und übertragen in die jeweils andere Seite.


Tastaturkürzel

Taste Aktion
Tab Zwischen lokalem und entferntem Paneel wechseln
↑ / ↓ Zeilenauswahl bewegen
→ Markierten Ordner/Eintrag öffnen
← / ⌫ Eine Verzeichnisebene höher
Enter Ordner öffnen (aktiv: fokussiertes Paneel)
↑ .. Eine Ebene höher (Tabellenzeile)
l Auswahl herunterladen (entfernt → lokal)
u Auswahl hochladen (lokal → entfernt)
n Neues Verzeichnis anlegen
d Auswahl löschen (rekursiv)
r Auswahl umbenennen
q / Esc Beenden

Hinweis: Hoch-/Herunterladen (u/l), Anlegen (n), Löschen (d) und Umbenennen (r) wirken auf das aktive (grün markierte) Paneel. Wechsel der Seite mit Tab. Die Pfeiltasten →/← navigieren die Verzeichnisstruktur (öffnen / eine Ebene hoch), ↑/↓ bewegen die Zeilenauswahl.


Projektstruktur

ftptui/
├── pyproject.toml          # Paketmetadaten, Abhängigkeiten, Einstiegspunkt
├── README.md               # diese Dokumentation
├── install.sh              # systemweite Installation (/opt/ftptui/venv)
├── uninstall.sh            # Deinstallation systemweiter Installation
├── .gitignore
└── ftptui/
    ├── __init__.py         # Versionsnummer
    ├── app.py              # Textual-TUI: Verbindungs- & Browser-Bildschirme
    ├── backend.py          # Fabrik: wählt FTP- oder SFTP-Transport
    ├── config.py           # Speicherung/Laden der Verbindungsprofile (JSON)
    ├── models.py           # Datentypen & Transport-Protokoll
    ├── ftp_backend.py      # FTP-Adapter (ftplib)
    └── sftp_backend.py     # SFTP-Adapter (paramiko)

Architektur

Die App trennt Oberfläche (Textual) von Transport (Netzwerkprotokolle) durch ein kleines Protokoll/Interface:

  • FileTransferBackend (in models.py) definiert die gemeinsame API.
  • FTPBackend und SFTPBackend implementieren sie.
  • create_backend(protocol) in backend.py liefert den passenden Adapter.

Gemeinsame Schnittstelle (Auszug):

def connect(host, port, username, password) -> None
def listdir(path)            -> list[RemoteEntry]
def chdir(path) / pwd()      -> str
def download(remote, local)  -> None
def upload(local, remote)    -> None
def mkdir(path) / remove / rmdir / rename
def close()                  -> None

RemoteEntry beschreibt einen Eintrag mit name, kind (file/dir), size und modified. Dadurch kann die Oberfläche unabhängig vom Protokoll arbeiten – das Hinzufügen weiterer Backends (z. B. WebDAV) ist ohne Änderung der UI möglich.

Pfadbehandlung

Beide Backends normalisieren Pfade zu absoluten Angaben relativ zum Root des entfernten Systems. FTP-Operationen setzen das Arbeitsverzeichnis vor einer Aktion auf /, sodass Dateiübertragungen unabhängig vom zuletzt betrachteten Ordner zuverlässig funktionieren. SFTP nutzt absolute Pfade direkt.

Profilwertung

Profile werden als JSON in ~/.config/ftptui/profiles.json (bzw. $XDG_CONFIG_HOME/ftptui/profiles.json) gespeichert. Das Passwort wird nur abgelegt, wenn es entsprechend markiert wurde (aktuell standardmäßig direkt gespeichert – Datenschutz-Hinweis siehe unten).


Konfiguration & Profile

Profile speichern: im Verbindungsbildschirm Daten eingeben und Profil speichern drücken. Gespeicherte Einträge erscheinen künftig im Dropdown Verbindungsprofil.

Beispieldatei ~/.config/ftptui/profiles.json:

[
  {
    "name": "git.sysdaemon.xyz",
    "protocol": "sftp",
    "host": "git.sysdaemon.xyz",
    "port": 22,
    "username": "user",
    "password": "",
    "save_password": false
  },
  {
    "name": "backup-server",
    "protocol": "ftp",
    "host": "backup.local",
    "port": 21,
    "username": "ftpuser",
    "password": "secret",
    "save_password": true
  }
]

Sicherheitshinweis: Speichern Sie Passwörter in Profilen nur auf vertrauenswürdigen Systemen. Die Datei liegt im Klartext im Benutzerverzeichnis.


Entwicklung

# Umgebung
python3 -m venv .venv && source .venv/bin/activate
pip install -e . "textual>=0.80.0" "paramiko>=3.4.0"

# Laufzeit prüfen
python -c "import ftptui; print(ftptui.__version__)"

# App booten (Konsolen-Test)
ftptui

Code-Stil & Qualität

  • Typannotationen (from __future__ import annotations) durchgängig.
  • Protokoll-/Interface-basierte Implementierung mit typing.Protocol.
  • Keine Abhängigkeit auf Drittanbieter außer textual und paramiko.

Testen

Die Backends werden gegen lokale Testserver end-to-end getestet:

  • FTP gegen pyftpdlib (nur für Tests; nicht Teil der Laufzeit-Abhängigkeiten),
  • SFTP gegen eine eigene paramiko-SSH-Server-Implementierung.

Ablauf der Tests: Verbinden, Listings, Download, Upload, mkdir, rename, remove, rekursives rmdir, close – für beide Protokolle.

pip install pyftpdlib
python tests/test_backends.py   # Beispieldatei (siehe Entwicklung/Skripte)

Ein ausführbares Testskript kann ergänzt und an dieser Stelle dokumentiert werden; die Backends sind derart modular, dass sie ohne Terminal getestet werden können.


Lizenz

MIT – siehe Projektträger/Repositories. Keine gewerblichen Einschränkungen.


Changelog

v0.3.0

  • Bugfix (Pfeil-Navigation): Im Dateibrowser navigieren die Pfeiltasten nun im File-Manager-Stil: ← wechselt eine Ebene nach oben, → öffnet den markierten Eintrag, ↑/↓ bewegen die Auswahl. Zuvor verschluckte das DataTable-Widget die Tasten (Widget-Bindings haben Vorrang). Dafür wird die neue FileTable-Klasse genutzt, die left/right passend umbindet.
  • Bugfix (Tab-Panel-Wechsel): Tab wechselt jetzt zuverlässig zwischen dem lokalen und dem entfernten Paneel (explizite action_focus_next statt unzuverlässigem focus_next).
  • Bugfix (_selected): Die Auswahlauswertung liefert jetzt die Klartext-Zeichenkette statt eines rich.Text-Objekts, wodurch Öffnen/Enter nicht mehr mit AttributeError abstürzte.
  • Neu: install.sh / uninstall.sh für systemweite Installation (PEP-668-konform via /opt/ftptui/venv + /usr/local/bin/ftptui).

v0.2.0

  • Bugfix: Wechsel in den Browser-Bildschirm korrigiert (set_screen → switch_screen). Vorher führte eine erfolgreiche Verbindung in einen AttributeError und der FTP/SFTP-Client beendete sich.
  • Theme: Umstellung auf ein klassisches Terminal-Design im Stil von ranger (Dark-Look, grün/fette Verzeichnisse, inverse Auswahl).
  • Layout: Neuaufbau als Total-Commander-artiger Dual-Pane-Browser mit Pfadleiste oben, Statusleiste unten und grün markiertem aktivem Panel.
  • Datums-Spalte wird nun als lesbares Datum (YYYY-MM-DD HH:MM) angezeigt.

v0.1.0

  • Erste Veröffentlichung: FTP- & SFTP-Backends, TUI, Verbindungsprofile.

Projekt wird innerhalb der sysdaemon.xyz-Instanz über Gitea verwaltet – https://git.sysdaemon.xyz/x3/ftptui.

S
Description
FTP/SFTP TUI-Client in Python mit SSH-FTP-Unterstützung
Readme
116 KiB
Languages
Python 91.9%
Shell 8.1%