Rekursiv Ordner erstellen: Stimmt das so?

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
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Donnerstag 11. September 2014, 10:18

Hallo,

ich möchte mit Paramiko einen Ordner erstellen. Leider hat dort die Funktion mkdir keine Option -p wie das unter dem Unix-mkdir der Fall wäre (Also dass auch alle übergeordneten Ordner erstellt werden). Daher habe ich notgedrungen so etwas selber erstellt. Da das mein erster Versuch mit Rekursion ist, wollte ich fragen, ob sich das jemand anschauen könnte und mir einige Tipps geben könnte? :) Also funktionierten tut der Code, aber vielleicht ist er etwas unschön.

Code: Alles auswählen

    def _mkdir(self, remote_path, stripped_remote_path=None):
        """
        Erstellt so lange Ordner mit self._sftp.mkdir, bis der gewünschte Ordner erstellt ist.
         (Paramikos mkdir hat keine -p Option wie mkdir von Unix, deswegen muss man die Ordner
         rekursiv erstellen).
        Siehe auch http://stackoverflow.com/questions/4409502/directory-transfers-on-paramiko

        :param remote_path: Der (eigentliche) Pfad, der erstellt werden soll.
        :param stripped_remote_path: Der um ein Segment verkürzte Pfad (nur wichtig für Rekursion)
        """

        if remote_path.endswith("/"):
            remote_path = remote_path[:-1]

        #Methode:
        #Zuerst wird versucht, den ursprünglichen Pfad zu erstellen. Wenn das nicht geht (wegen
        #IO-Error), wird ein Ordnersegment abgezogen und diese Funktion wieder aufgerufen
        #(Rekursion), mit dem zusätzlichen Argument stripped_remote_path. Anschließend wird im
        #zweiten Durchlauf versucht, den gekürzten Ordner zu erstellen. Wenn das geht, wird die
        #Funktion wieder wie zu Beginn aufgerufen. Ansonsten wird noch ein Ordnersegment abgezogen.
        try:
            if stripped_remote_path is None:
                self._sftp.mkdir(remote_path)
            else:
                self._sftp.mkdir(stripped_remote_path)
                self._mkdir(remote_path)
        except IOError as e:
            if stripped_remote_path is None:
                stripped_remote_path = remote_path
            self._mkdir(remote_path, stripped_remote_path.rsplit("/", 1)[0])

Danke!
BlackJack

Donnerstag 11. September 2014, 10:44

@Hellstorm: Ich sehe denn Sinn von Rekursion als Sprachmittel hier nicht so wirklich. Das kann man doch auch in einer Schleife lösen.

Es erscheint mir auch ineffizient, denn wenn `n` Pfadelemente fehlen dann hast Du da `n²/2` Versuche Verzeichnisse anzulegen, wo eigentlich `2n` ausreichen würden, nämlich `n` um heraus zu finden wie viele fehlen, und weitere `n` um die dann anzulegen. Vielleicht stelle ich mich auch zu sehr an, aber bei Algorithmen mit unterschiedlichen Komplexitätsklassen würde ich die bessere Variante bevorzugen, zumindest wenn die nicht deutlich komplizierter zu implementieren ist.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Donnerstag 11. September 2014, 10:59

Hm, für etwas ineffizient hatte ich das auch gehalten. Insbesondere weil er das ganze wieder neu anfängt, anstatt nur in die andere Richtung zurück zu gehen.

Gut, ich probiere es mal in einer Schleife, danke. Irgendwie hatte ich mich da zu sehr auf Rekursion eingeschossen („Yeah, endlich hab ich mal ein Szenario wo ich das anwenden kann!“ :D), dass ich an Schleifen nicht gedacht habe.

Ich schreibe dann meine Lösung wieder hier hinein, wenn sie fertig ist.
sfx2k
User
Beiträge: 48
Registriert: Dienstag 2. September 2014, 13:29

Donnerstag 11. September 2014, 11:36

Huhu,

mag sein, dass ich Dein Vorhaben falsch verstanden habe, aber warum machst Du es nicht einfach so?

Code: Alles auswählen

def makedirs(path):

    '''
    Funktion:
        Erzeugt ein Verzeichnis inkl. der übergeordneten Struktur
        Ignoriert Fehler, wenn Verzeichnis bereits vorhanden

    Paramter:
        path: Pfad zum anzulegenden Verzeichnis

    Rückgabe:
        Boolean
    '''

    try:
        os.makedirs(path)
    except Exception:
        pass

    return os.path.isdir(path)
Edit: ah, ok - jetzt habe ich es. Das Verzeichnis soll remote angelegt werden :D
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Donnerstag 11. September 2014, 20:37

Hallo,

so besser?

Code: Alles auswählen

    def _mkdir(self, remote_path):
        if remote_path.endswith("/)"):
            remote_path = remote_path[:-1]

        cut_length = 1

        for i in range(len(remote_path.rsplit("/"))-1):
            try:
                self._sftp.stat(remote_path.rsplit("/", i)[0])
                cut_length = i
                break
            except FileNotFoundError as e:
                pass

        for i in range(cut_length-1, -1, -1):
            self._sftp.mkdir(remote_path.rsplit("/", i)[0])
Der trennt jetzt mit rsplit() den Pfad rückwärts immer einen Ordner weiter auf und guckt, ob der Ordner vorhanden ist. Wenn der vorhanden ist, merkt er sich die Stelle und springt aus der Schleife. In einer nächsten Schleife erstellt er dann ab der Trennstelle von vorne die fehlenden Ordner.
Sirius3
User
Beiträge: 8572
Registriert: Sonntag 21. Oktober 2012, 17:20

Freitag 12. September 2014, 07:04

Der Pfad wird ein bißchen oft gesplittet. Man könnte sich die nicht vorhandenen Pfade gleich merken:

Code: Alles auswählen

    def mkdir(self, remote_path):
        pathes_to_create = []
        remote_path = remote_path.rstrip('/')
        while remote_path:
            try:
                self._sftp.stat(remote_path)
            except FileNotFoundError:
                pathes_to_create.append(remote_path)
                remote_path, _ = remote_path.rsplit('/', 1)
            else:
                break
        for path in reversed(pathes_to_create):
            self._sftp.mkdir(path)
Antworten