2013-03-22 16 views
26

Ich muss einen Symlink für jedes Element von dir1 (Datei oder Verzeichnis) innerhalb von dir2 erstellen. dir2 existiert bereits und ist kein Symlink. In Bash kann ich erreichen, dies leicht durch:`os.symlink` vs` ln -s`

ln -s /home/guest/dir1/* /home/guest/dir2/

Aber in Python mit os.symlink mir eine Fehlermeldung erhalten:

>>> os.symlink('/home/guest/dir1/*', '/home/guest/dir2/') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
OSError: [Errno 17] File exist 

Ich weiß, ich subprocess verwenden kann und ln Befehl ausführen. Ich will diese Lösung nicht.

Ich bin auch bewusst, dass Abhilfen mit os.walk oder glob.glob möglich sind, aber ich möchte wissen, ob es möglich ist, diese os.symlink mit zu tun.

+3

Ich denke, * * 'os.symlink' eine bloße Umhüllung des jeweiligen Systemaufruf ist (na ja, mehr oder weniger) und damit die gleiche Semantik nicht bieten würde man von einem Voll bekommen flügge Dienstprogramm mit diesem Systemaufruf. – 0xC0000022L

+1

Was ist der Grund für die Anforderung "Ich möchte dies mithilfe von' os.symlink "tun? Wenn es richtig ist, eine 'for'-Schleife über einen' glob' zu machen, warum willst du dann nicht das Richtige tun? – abarnert

+0

@abarnert Ich werde das tun, wenn ich muss. Ich wollte wissen, ob es einen Weg gibt, es mit 'os.symlink' zu machen. Ich dachte, es würde alles liefern, was "ln -s" tut, aber es stellt sich heraus, dass es nicht so ist. – jurgenreza

Antwort

45

os.symlink erstellt einen einzelnen Symlink.

ln -s erstellt mehrere symbolische Verknüpfungen (wenn das letzte Argument ein Verzeichnis ist und mehrere Quellen vorhanden sind). Das Python-Äquivalent ist so etwas wie:

dst = args[-1] 
for src in args[:-1]: 
    os.symlink(src, os.path.join(dst, os.path.dirname(src))) 

Also, wie funktioniert es, wenn Sie ln -s /home/guest/dir1/* /home/guest/dir2/ tun? Ihre Shell macht das, indem Sie den Platzhalter in mehrere Argumente verwandeln. Wenn Sie nur exec der ln Befehl mit einem Platzhalter wären, würde es nach einer einzelnen Quelle mit dem tatsächlichen Namen * in /home/guest/dir1/ suchen, nicht alle Dateien in diesem Verzeichnis.

Das Python-Äquivalent ist so etwas wie (wenn Sie nichts dagegen haben zwei Ebenen miteinander zu vermischen und eine Menge anderer Fälle-Tilden ignorieren, env Variablen, Kommandosubstitution usw., die an der Schale sind möglich):

dst = args[-1] 
for srcglob in args[:-1]: 
    for src in glob.glob(srcglob): 
     os.symlink(src, os.path.join(dst, os.path.dirname(src))) 

Sie können das nicht mit os.symlink allein tun - entweder ein Teil davon - weil es das nicht tut. Es ist so, als würde ich sagen: "Ich möchte das Äquivalent von find . -name foo mit os.walk machen, ohne den Namen zu filtern." Oder, in der Tat, ich möchte das Äquivalent von ln -s /home/guest/dir1/* /home/guest/dir2/ ohne die Shell Globbing für mich tun."

Die richtige Antwort ist glob zu verwenden, oder fnmatch oder os.listdir sowie eine regex, oder was auch immer Sie bevorzugen.

Sie nicht Verwendung os.walk, denn das ist ein rekursive Dateisystem tut zu Fuß, so dass es nicht einmal in der Nähe * Expansion zu schälen.

+0

+1 vielen Dank – jurgenreza

12

* ist ein Shell-Erweiterungsmuster, das in Ihrem Fall "alle Dateien beginnend mit /home/guest/dir1/" bezeichnet.

Aber es ist Ihre Shell-Rolle, dieses Muster zu erweitern zu den Dateien, die es entspricht. Nicht der ln Befehl.

Aber os.symlink ist keine Shell, es ist ein OS-Aufruf - daher unterstützt es keine Shell-Erweiterungsmuster. Sie müssen diese Arbeit in Ihrem Skript erledigen.

Um dies zu tun, können Sie os.walk oder os.listdir verwenden. Wie in der anderen Antwort angegeben, hängt der entsprechende Anruf davon ab, was Sie tun möchten. (os.walk wäre nicht das Äquivalent von * sein)


sich zu überzeugen: führen Sie diesen Befehl auf einem Unix-Rechner in Ihrem Terminal: python -c "import sys; print sys.argv" *. Du wirst sehen, dass es die Shell ist, die den Abgleich durchführt.

+0

+1 für das Experiment über das Globbing. Beachten Sie jedoch, dass die Windows-Shell (cmd.exe) dies nicht tut, bevor sie an das aufgerufene Programm übergeben wird. – 0xC0000022L

+0

+1 ok fair genug. :) – jurgenreza

+0

@ 0xC0000022L: Nun, um fair zu sein, Windows kommt nicht mit "ln". Und MSVCRT (ihre libc) kommt mit einem Hack, um Shell-Globbing auf 'argv' zu simulieren, entweder bevor Ihr' main' startet, oder wenn Sie explizit danach fragen, und ich bin ziemlich sicher, dass die meisten Ports von Unix-Programmen entweder das verwenden oder etwas Äquivalentes (zB was auch immer Cygwin macht). – abarnert

3

Wie vorgeschlagen @abarnert es ist die Schale, die * und ersetzt sie mit allen Elementen insside dir1 erkennt. Deshalb denke ich, mit os.listdir ist die beste Wahl:

for item in os.listdir('/home/guest/dir1'): 
    os.symlink('/home/guest/dir1/' + item, '/home/guest/dir2/' + item) 
+4

Ich empfehle die Verwendung von 'os.path.join' anstelle von' + '. –

Verwandte Themen