J'ai besoin de créer un lien symbolique pour chaque élément de dir1 (fichier ou répertoire) à l'intérieur de dir2. dir2 existe déjà et n'est pas un lien symbolique. Dans Bash, je peux facilement y parvenir en:
ln -s /home/guest/dir1/* /home/guest/dir2/
Mais en python en utilisant os.symlink
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 la commande ln
. Je ne veux pas de cette solution.
Je suis également conscient que les 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
.
os.symlink
crée un seul lien symbolique.
ln -s
crée plusieurs liens symboliques (si son dernier argument est un répertoire et qu'il existe plusieurs sources). 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 simplement exec
la commande ln
avec un caractère générique, elle chercherait une seule source nommée littéralement *
dans /home/guest/dir1/
, pas tous les fichiers de ce 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 sur le Shell ):
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 ça avec os.symlink
seul, soit une partie de celui-ci, car il ne fait pas cela. 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 que le coquillage ne me touche. "
La bonne réponse consiste à utiliser glob
, ou fnmatch
, ou os.listdir
plus une expression régulière, ou ce que vous préférez.
N'utilisez pas os.walk
, car cela fait un parcours du système de fichiers récursif , donc il n'est même pas proche de Shell *
expansion.
*
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 votre le rôle de Shell pour développer ce modèle aux fichiers auxquels il correspond. Pas la commande ln
.
Mais os.symlink
n'est pas un Shell, c'est un appel au système d'exploitation - par conséquent, il ne prend pas en charge 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 approprié dépendra 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 dans votre terminal: python -c "import sys; print sys.argv" *
. Vous verrez que c'est le Shell qui fait l'appariement.
Comme suggéré par @abarnert, c'est le Shell qui reconnaît *
et le remplace par tous les éléments situés à l'intérieur de dir1. Je pense donc qu'en utilisant os.listdir
est le meilleur choix:
for item in os.listdir('/home/guest/dir1'):
os.symlink('/home/guest/dir1/' + item, '/home/guest/dir2/' + item)