The following code is used to copy the symlink.

if os.path.islink(src_path): linkto = os.readlink(src_path) os.symlink(linkto, dest_path) 

however, os.path.islink and os.readlink do not know how to work with the junction point

How to recognize junction point and recreate it in another place?

  • islink and readlink are different functions — correct the header (obviously, islink () is not always False — run test-symlink.py ). ctypes : it is not necessary to use ctypes here: ( os.lstat(filename).st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT ). - jfs
  • After running the script and getting OSError: symbolic link privilege not held and few checks, I realized that python sees the links created via mklink / D (they require admin privileges by the way), but does not understand the "junction" created via mklink / j, although I don’t see any difference in the behavior of the created "links" - vitidev
  • therefore, the script text contains: "requires SeCreateSymbolicLinkPrivilege" opposite os.symlink() —simply as an administrator to run, of course. There are three concepts: symbolic links, junction points, mount points under the umbrella of reparse points. That is, junction is different from symlink and the presence of FILE_ATTRIBUTE_REPARSE_POINT not enough: not islink() ( dwReserved0 : IO_REPARSE_TAG_MOUNT_POINT vs. IO_REPARSE_TAG_SYMLINK different at win32 level) stdlib in a different way now belongs to junction: in one place it is the rmdir (path) directory, in another the link is unlink (). - jfs
  • over time, we can expect that at the Python level, the junction will increasingly behave like symlink on other systems. For example, now os.walk(root, follow_symlinks=False) still goes through the junction, but this may change - jfs

1 answer 1

In Python 3.5, os.path.islink() , os.readlink() , os.symlink() work only with symbolic links as documented . NTFS symbolic links (to files / folders) that model Unix symlinks are available to user applications starting from Windows Vista .

NTFS junctions is a close (directory links) but different concept (privileges, remote paths) supported with Windows 2000 , although the actual creation / deletion of the Junction required installation of additional utilities and standard utilities, such as creating backups, could break on the folders containing junctions . By changing the names of the standard folders between different versions of Windows and using junction points to support backward compatibility , Junction is quite common now (run DIR /AL /S in cmd to see examples).

From the point of view of many applications, Junction is a regular folder. In the context of backup, to avoid looping or copying the same files several times, to the junction points should be treated similarly to symbolic links on other systems.

Both symbolic links and Junction are implemented as reparse points, so the code in question, when they point to directories, can be replaced by (not tested):

 if is_reparse_point(src_path): # symlinkd, junction, etc try: linkto = os.readlink(src_path) except ValueError: # not a symlink linkto = readlink(src_path) # junction, mount point create_junction(linkto, dest_path) else: os.symlink(linkto, dest_path, target_is_directory=True) 

Where possible implementations of functions that support junction points:

 import errno import os import stat from tkinter import Tcl from _winapi import CreateJunction as create_junction def is_reparse_point(path): try: # | stat.FILE_ATTRIBUTE_DIRECTORY) return bool(os.lstat(path).st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT) except OSError as e: if e.errno not in (errno.ENOENT, errno.ENOTDIR): raise return False # path doesn't exist def readlink(path): assert '"' not in path return Tcl().eval('file readlink "%s"' % path.replace('\\', '\\\\')) 

Where:

  • create_junction() is an analogue of mklink / J
  • is_reparse_point() can return True not only for Junction, but also for a symbolic link, mount point ( os.path.ismount() ) and (possibly) other objects .

    Formally, a symbolic link is FILE_ATTRIBUTE_REPARSE_POINT with WIN32_FIND_DATA.dwReserved0 equal to IO_REPARSE_TAG_SYMLINK , and junction is IO_REPARSE_TAG_MOUNT_POINT , which is not mount point

  • readlink() returns the path to the folder where Junction points to (like os.readlink() for symlink). For simplicity, tcl is used to get this path, otherwise an implementation using ctypes is ctypes :

     >>> readlink(r'C:\Documents and Settings') 'C:\\Users' 

Alternatively, there are PyPI modules of varying degrees of polishing and applicability, depending on the case, for example ntfsutils , jaraco.windows , providing similar functionality. In the future, perhaps os.path.islink() and os.readlink() will also support Directory Junction directly .

  • Here's another question from the English-speaking SO, where the working code that works with symlinks and junction is equally good is stackoverflow.com/questions/27972776/… - The Godfather
  • @TheGodfather: my answer already contains a link to the answer from this question. - jfs