`OS.lien symbolique` vs `ln-s`

J'ai besoin de créer un lien symbolique pour chaque élément de dir1 (fichier ou répertoire) dans dir2. dir2 existe déjà et n'est pas un lien symbolique. En Bash, je peux facilement y parvenir en:

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

Mais en python en utilisant os.lien symbolique je reçois une erreur:

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

Je sais que je peux utiliser subprocess et exécuter ln commande. Je ne veux pas que la solution.

Je suis également conscient que des solutions de contournement utilisant os.walk ou glob.glob sont possibles, mais je veux savoir s'il est possible de le faire en utilisant os.symlink.

31
demandé sur jurgenreza 2013-03-23 01:50:03

3 réponses

os.symlink crée un lien symbolique.

ln -s crée plusieurs liens symboliques (si son dernier argument est un répertoire et qu'il y a plus d'une source). L'équivalent Python est quelque chose comme:

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

Alors, comment ça marche quand vous faites ln -s /home/guest/dir1/* /home/guest/dir2/? Votre shell fait que cela fonctionne, en transformant le caractère générique en plusieurs arguments. Si vous deviez juste exec la commande ln avec un caractère générique, il chercherait une source unique littéralement nommée * dans /home/guest/dir1/, pas tous les fichiers dans cela répertoire.

L'équivalent Python est quelque chose comme (si cela ne vous dérange pas de mélanger deux niveaux ensemble et d'ignorer beaucoup d'autres cas-tildes, variables env, substitution de commandes, etc. qui sont possibles à la coquille):

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)))

Vous ne pouvez pas faire cela avec os.symlink seul-ou partie de celui-ci-parce qu'il ne le fait pas. C'est comme dire "je veux faire l'équivalent de find . -name foo en utilisant os.walk sans filtrer sur le nom."Ou, d'ailleurs, je veux faire l'équivalent de ln -s /home/guest/dir1/* /home/guest/dir2/ sans le coquille d'expansion pour moi."

, Le droit de réponse est d'utiliser glob, ou fnmatch, ou os.listdir, plus une regex, ou ce que vous préférez.

Faites Pas utilisez os.walk, car cela fait une marche récursive du système de fichiers, donc ce n'est même pas proche de l'expansion du shell *.

51
répondu abarnert 2013-03-22 21:59:47

* est un modèle d'extension shell, qui dans votre cas désigne " tous les fichiers commençant par /home/guest/dir1/".

Mais c'est le rôle de votre shell d'étendre ce modèle aux fichiers qu'il correspond. Pas la commande ln.

Mais os.symlink n'est pas un shell, c'est un appel OS - par conséquent, il ne supporte pas les modèles d'extension shell. Vous devrez faire ce travail dans votre script.

Pour ce faire, vous pouvez utiliser os.walk, ou os.listdir. Comme indiqué dans l'autre réponse, l'appel sera dépendent de ce que vous voulez faire. (os.walk ne serait pas l'équivalent de *)


Pour vous convaincre: exécutez cette commande sur une machine Unix de votre terminal: python -c "import sys; print sys.argv" *. Vous verrez que c'est le shell qui fait la correspondance.

12
répondu Thomas Orozco 2013-03-22 22:47:17

Comme suggéré par @abarnert c'est le shell qui reconnaît * et le remplace par tous les éléments insside dir1. Par conséquent, je pense que l'utilisation de os.listdir est le meilleur choix:

for item in os.listdir('/home/guest/dir1'):
    os.symlink('/home/guest/dir1/' + item, '/home/guest/dir2/' + item)
3
répondu jurgenreza 2013-05-03 20:14:01