Windows 中的 OS.symlink 支持

发布于 2024-11-14 08:02:13 字数 187 浏览 7 评论 0原文

我从python网站下载了python 2.7.1并将其安装到windows上。当尝试符号链接文件时,我发现它不受支持。

但是,我发现这个问题,并发现它已得到修复。这会实施吗?如果实施的话,什么时候实施?我运行的是 Windows Vista。

I downloaded python 2.7.1 from the python website and installed it to windows. When attempting to symlink a file, I find that it is not supported.

However, I found this issue, and saw that it was fixed. Will this be implemented, and if so when? I'm running windows Vista.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

半仙 2024-11-21 08:02:13

有一种方法可以解决这个问题,在你的 python 环境启动时修补 os 模块。

Windows API 已经提供了创建符号链接的函数,您只需要调用它即可。

在 python 启动期间,会尝试在 站点包目录。我们将使用这个钩子将我们的函数附加到 os 模块。

将此代码放在文件 sitecustomize.py 中:

import os

__CSL = None
def symlink(source, link_name):
    '''symlink(source, link_name)
       Creates a symbolic link pointing to source named link_name'''
    global __CSL
    if __CSL is None:
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        __CSL = csl
    flags = 0
    if source is not None and os.path.isdir(source):
        flags = 1
    if __CSL(link_name, source, flags) == 0:
        raise ctypes.WinError()

os.symlink = symlink

您的 Python 进程需要以启用的“创建符号链接”权限启动,这不是 Python 问题,每个声称使用此 Windows API 的程序都需要它。这可以通过从提升的 cmd.exe 运行 Python 解释器来完成。更好的替代方法是向用户授予权限,前提是您的 Windows 版本附带所需的组策略编辑器 (gpedit.msc)。请参阅下面的屏幕截图。您可以调整该值以包含需要此类权限的任何用户或安全组,而不会影响管理帐户的安全性。

组策略编辑器

注意: 代码片段来自 此处

There's a way to fix this, patching the os module on your's python environment start.

The function to create symlinks is already avaliable from Windows API, you only need do call it.

During python's startup, an attempt is made to import a module named sitecustomize.py, on the site-packages directory. We will use this hook to attach our function to the os module.

Put this code on the file sitecustomize.py:

import os

__CSL = None
def symlink(source, link_name):
    '''symlink(source, link_name)
       Creates a symbolic link pointing to source named link_name'''
    global __CSL
    if __CSL is None:
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        __CSL = csl
    flags = 0
    if source is not None and os.path.isdir(source):
        flags = 1
    if __CSL(link_name, source, flags) == 0:
        raise ctypes.WinError()

os.symlink = symlink

Your Python process needs to be started with enabled "Create symbolic links" privilege, this is not a Python issue, every program that claims to use this Windows API will need it. This can be done running your Python interpreter from an elevated cmd.exe. A better alternative is to grant the privilege to the user, provided your Windows edition ships with the required Group Policy editor (gpedit.msc). See the screenshot below. You can adjust the value to include whatever user or security group requires this kind of privilege without compromising on the security of the administrative accounts.

The group policy editor

Note: Code snippet from here

只涨不跌 2024-11-21 08:02:13

就像费尔南多·马塞多的回答一样,但在我看来,侵入性较小:

def symlink(source, link_name):
    import os
    os_symlink = getattr(os, "symlink", None)
    if callable(os_symlink):
        os_symlink(source, link_name)
    else:
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        if csl(link_name, source, flags) == 0:
            raise ctypes.WinError()

Like the Fernando Macedo answer, but IMO less invasive:

def symlink(source, link_name):
    import os
    os_symlink = getattr(os, "symlink", None)
    if callable(os_symlink):
        os_symlink(source, link_name)
    else:
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        if csl(link_name, source, flags) == 0:
            raise ctypes.WinError()
挽你眉间 2024-11-21 08:02:13

类似于 Gian Marco Gherardi answer,但在 Windows 上定义了 os.symlink,以便您的代码可以安全地在 Windows 和 Linux 上工作:

import os
os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    def symlink_ms(source, link_name):
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        if csl(link_name, source, flags) == 0:
            raise ctypes.WinError()
    os.symlink = symlink_ms

如果您以管理员身份运行脚本,一切都很好,如果您想以用户身份运行它 - 您必须 授予Python创建符号链接的权限——这只能在Windows Vista+ Ultimate或Professional下进行。

编辑

Gian Marco Gherardi 答案创建一个指向unix路径的链接:like/这 并不起作用。修复方法是执行 source.replace('/', '\\')

# symlink support under windows:
import os
os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    def symlink_ms(source, link_name):
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        if csl(link_name, source.replace('/', '\\'), flags) == 0:
            raise ctypes.WinError()
    os.symlink = symlink_ms

另一种方法是使用 Windows 的 vista+ mklink 实用程序。但使用此实用程序需要相同的权限。还是:

# symlink support under windows:
import os
os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    def symlink_ms(source, link_name):
        os.system("mklink {link} {target}".format(
            link = link_name,
            target = source.replace('/', '\\')))
    os.symlink = symlink_ms

编辑 2

这就是我最终使用的:如果用户有权限,此脚本会在 Windows 下创建链接,否则它不会创建链接:

import os
if os.name == "nt":
    def symlink_ms(source, link_name):
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        try:
            if csl(link_name, source.replace('/', '\\'), flags) == 0:
                raise ctypes.WinError()
        except:
            pass
    os.symlink = symlink_ms

Like Gian Marco Gherardi answer but defines os.symlink on windows, so that your code can safely work on windows and linux:

import os
os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    def symlink_ms(source, link_name):
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        if csl(link_name, source, flags) == 0:
            raise ctypes.WinError()
    os.symlink = symlink_ms

If you run your script as administrator everything is fine, if you want to run it as user -- you have to grant python a permission to make symlinks -- which only possible under windows vista+ ultimate or professional.

Edit:

Gian Marco Gherardi answer creates a link to a unix path: like/this and it doesn't work. The fix is to do source.replace('/', '\\'):

# symlink support under windows:
import os
os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    def symlink_ms(source, link_name):
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        if csl(link_name, source.replace('/', '\\'), flags) == 0:
            raise ctypes.WinError()
    os.symlink = symlink_ms

Another way is to use window's vista+ mklink utility. But using this utility requires same permissions. Still:

# symlink support under windows:
import os
os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    def symlink_ms(source, link_name):
        os.system("mklink {link} {target}".format(
            link = link_name,
            target = source.replace('/', '\\')))
    os.symlink = symlink_ms

Edit 2:

Here's what I'm finally using: this script makes a link under windows if the user has a privilage to do so, otherwise it just doesn't make a link:

import os
if os.name == "nt":
    def symlink_ms(source, link_name):
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        try:
            if csl(link_name, source.replace('/', '\\'), flags) == 0:
                raise ctypes.WinError()
        except:
            pass
    os.symlink = symlink_ms
最近可好 2024-11-21 08:02:13

开发者模式下的 Windows 10 可以通过根据 Windows 文档。我唯一的问题是删除任何现有链接仍然需要提升权限。

os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    print "Patching windows symlink support"
    def symlink_ms(source, link_name):
        import ctypes
        import ctypes.wintypes as wintypes
        if os.path.exists(link_name):
            df = ctypes.windll.kernel32.DeleteFileW
            if df(link_name) == 0:
                print "Could not remove existing file:", link_name
                print "You should remove the file manually through Explorer or an elevated cmd process."
                raise ctypes.WinError()
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        flags += 2 # For unprivileged mode. Requires Developer Mode to be activated.
        if csl(link_name, source, flags) == 0:
            raise ctypes.WinError()
    os.symlink = symlink_ms

Windows 10 in developer mode can create symlinks without elevated privileges by setting the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE flag as per the Windows documentation. My only issue is that deleting any existing link still needs elevated privileges.

os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
    pass
else:
    print "Patching windows symlink support"
    def symlink_ms(source, link_name):
        import ctypes
        import ctypes.wintypes as wintypes
        if os.path.exists(link_name):
            df = ctypes.windll.kernel32.DeleteFileW
            if df(link_name) == 0:
                print "Could not remove existing file:", link_name
                print "You should remove the file manually through Explorer or an elevated cmd process."
                raise ctypes.WinError()
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        flags = 1 if os.path.isdir(source) else 0
        flags += 2 # For unprivileged mode. Requires Developer Mode to be activated.
        if csl(link_name, source, flags) == 0:
            raise ctypes.WinError()
    os.symlink = symlink_ms
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文