`os.symlink` vs` ln -s`

Necesito crear un enlace simbólico para cada elemento de dir1 (archivo o directorio) dentro de dir2. dir2 ya existe y no es un enlace simbólico. En Bash puedo lograr esto fácilmente por:

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

Pero en Python usando os.symlink me sale un error:

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

Sé que puedo usar subprocess y ejecutar el comando ln . No quiero esa solución.

También soy consciente de que las soluciones alternativas que utilizan os.walk o glob.glob son posibles, pero quiero saber si es posible hacerlo utilizando os.symlink .

os.symlink crea un enlace simbólico único.

ln -s crea varios enlaces simbólicos (si su último argumento es un directorio y hay más de una fuente). El equivalente de Python es algo como:

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

Entonces, ¿cómo funciona cuando haces ln -s /home/guest/dir1/* /home/guest/dir2/ ? Su shell hace que funcione, al convertir el comodín en múltiples argumentos. Si solo exec el comando ln con un comodín, buscaría una sola fuente literalmente llamada * en /home/guest/dir1/ , no todos los archivos en ese directorio.

El equivalente de Python es algo así (si no te importa mezclar dos niveles e ignorar muchos otros casos (tildes, variables env, sustitución de comandos, etc., que son posibles en el 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))) 

No puedes hacer eso solo con os.symlink , ya sea parte de él, porque no hace eso. Es como decir “Quiero hacer el equivalente de find . -name foo usa os.walk sin filtrar el nombre”. O, para el caso, quiero hacer el equivalente de ln -s /home/guest/dir1/* /home/guest/dir2/ sin el shell globbing para mí “.

La respuesta correcta es usar glob , o fnmatch , o os.listdir más una expresión regular, o lo que prefiera.

No use os.walk , ya que eso hace un recorrido recursivo del sistema de archivos, por lo que ni siquiera está cerca de la expansión de shell * .

* es un patrón de extensión de shell, que en su caso designa “todos los archivos que comienzan con /home/guest/dir1/ “.

Pero es función de su shell expandir este patrón a los archivos que coincide. No es el comando.

Pero os.symlink no es un shell, es una llamada del sistema operativo, por lo tanto, no admite los patrones de extensión del shell. Tendrás que hacer ese trabajo en tu guión.

Para hacerlo, puede usar os.walk , o os.listdir . Como se indica en la otra respuesta, la llamada apropiada dependerá de lo que desee hacer. ( os.walk no sería el equivalente de * )


Para convencerse: ejecute este comando en una máquina Unix en su terminal: python -c "import sys; print sys.argv" * . Verás que es la concha la que hace la correspondencia.

Según lo sugerido por @abarnert, es el shell que reconoce * y lo reemplaza con todos los elementos dentro de dir1. Por lo tanto, creo que usar os.listdir es la mejor opción:

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