有没有办法创建从 Windows 机器上的文件夹到 Linux 机器上的目录的符号链接?

有没有办法创建从 Windows 机器上的文件夹到 Linux 机器上的目录的符号链接?

我有一个在 Windows VirtualBox VM 上运行的程序,并将数据存储在特定文件夹中。我想从这个文件夹到 Linux 服务器上的目录建立类似符号链接的东西,但我还没有想出可行的解决方案。我尝试将 Linux 文件夹结构映射到驱动器号,然后从“原始”Windows 文件夹到映射到驱动器号的文件夹建立连接,但 Windows 不允许我完成链接。我还尝试使用 SFTP 连接 Linux 目录,但也没有成功。

答案1

这是我所做的。

首先,我在 Linux 机器上安装了 SAMBA,并共享了我想要使用的驱动器。(这是一个完全不同的话题,但你可以找到很多关于如何做到这一点的描述。)

然后,在 Windows 框上,我创建了下面的 Python 3 类来建立链接。

它还可用于创建到 Windows 共享的链接。

但简短的回答是 - 在 Linux 机器上创建共享并在命令提示符中使用 mklink 创建符号链接。

警告:此代码仅用于概念验证。它尚在开发中,尚未完成。我并不认为这是最好的代码,甚至不是好的代码。

"""    
@Author: J. Michael Adams

Please cite the author's name if you use any of this code.

Creates symlinks for share folders

It places all the symlinks in a local folder. The test code defines that folder
as C:\share_symlinks.

NOTE -
This program uses the "mklink" command of the Windows command shell.
That command normally requires elevated administrator privilege.
That requirement can be changed using the "secpol.msc" console under
Security Settings > Local Policies > User Rights Assignment > create symbolic links

It pipes in a list of shares from the "net view" command.

Each output line from net view has this format: share-name "Disk" other-info
If it does not contain " Disk ", then it does not have a share name.

We want to create a symlink for each share.
The links will be created in a particular directory.

You can specify a list of share names that will be excluded - that is, they will
not be linked to. A name can can contain wildcards.

Any share name that matches a name in the exclude list will be ignored.
"""

#TODO: create a config file: excludes, link_dir, remote_drive, notify always, email parms
#TODO: check the permission level
#TODO: check the value of comspec
#TODO: remove obsolete links - links that have the proper remote_drive
#TODO: create an email object for notification
#TODO: create an exception that emails a notification

import os
import subprocess

class LinkManager(object):

    def __init__(self, remote_drive):
        self.remote_drive = remote_drive

        self.share_paths = {}  # share_paths[share name] => full path to share
        self.new_links = {}
        self.all_links = {}
        self.bad_links = {}

    def get_shares(self, excludes=None):

        """ returns a dict: key = share name, value = full path to the share

        """

        import fnmatch

        if type(excludes) is not list:
            if excludes is None:
                excludes = []
            elif type(excludes) is str:
                excludes = [excludes]
            else:
                raise Exception

        # Always exclude shares that end with '$'. These are administrative shares in Windows.
        excludes.append('*$')
        # We want to ignore case when we compare strings in the excludes list.
        # So we'll convert all share names to lower case.
        excludes = [x.lower() for x in excludes]

        ## call net view for the share drive. This might produce "access denied".
        # http://stackoverflow.com/questions/3005437/windowserror-error-5-access-is-denied
        cmd = "net view {} /all".format(remote_drive)

        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                shell=True, universal_newlines=True)
        (out, err) = proc.communicate()

        if err:
            return out, err  #TODO: should be an exception

        ## get the output of the command and parse the share names
        # we'll convert all lines to lower case.
        # Since this is Windows, file names are case insensitive.
        # We do this so we can compare to the lower case names in the exclude list.
        lines = out.lower().split('\n')

        for line in lines:

            # search for " disk " surrounded by a space, in case "disk" is a share name.
            if " disk " not in line:
                continue

            share = line.split(' disk ')[0].strip()

            # Check the share name against the exclude list.
            # The list can have names with wildcards ('*' and '?'),
            # so we'll use fnmatch() to check it.
            found = False
            for exclude in excludes:
                if fnmatch.fnmatch(share, exclude):
                    found = True
                    break
            if found:
                continue

            self.share_paths[share] = os.path.join(remote_drive, share)

        return '', ''

    def make_links(self, link_dir):

        """
         link_dir is the full path to the directory that will contain the links
         self.share_paths is a dict: share-name => target

         returns 3 dicts:
                new_links: a dict of all newly created links,
                all_links: a dict of all links in the link directory
                bad_links: links that do not point to the share base.

                key = link (full path)
                value = target (full path)

                for bad_link: if the value is None, the link path is not a link
         a dict of created links:
        """

        result = []
        for share, path in self.share_paths.items():

            # Create a symlink to the link directory.

            link = os.path.join(link_dir, share)

            self.all_links[link] = path

            # If it's already a link, it should point to the proper place
            # If the link name exists, but it's not a link
            # it's an error (or at least an issue).
            if os.path.exists(link):
                if os.path.islink(link):
                    relative_target = os.readlink(link)
                    target = os.path.realpath(relative_target)
                    if target != path:
                        self.bad_links[link] = target
                else:
                    self.bad_links[link] = None
                continue

            proc = subprocess.Popen(["mklink", "/D", link, path],
                                    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                    shell=True)
            (out, err) = proc.communicate()
            #TODO: check the output. err should be empty. out should not be.
            print("program output: ", out)
            print("program err: ", err)

            self.new_links[link] = path

            result.append((out, err))

        return

    def remove_obsolete(self):

        # for each link in the directory, see if the name is a share. If not, remove it.
        #TODO: code it.
        pass
    def remove_link(self, link):

        if os.path.islink(link):
            # This removes the link, not the target
            proc = subprocess.Popen(["rmdir", link], stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE, shell=True)
            (out, err) = proc.communicate()
        else:
            out = ''
            err = "{} is not a link".format(link)

        return out, err

    # send an email to server_notifications


############## TEST ############################

#############################
# define the shares that will not be linked to.
# The routine "get_shares() will add the entry '*$' to the
# exclude list.
#############################

excludes = ['trash', 'home', 'Users']


link_dir = r'C:\share_symlinks'
remote_drive = r'\\bilbao'

mgr = LinkManager(remote_drive)

mgr.get_shares(excludes)
mgr.make_links(link_dir)

testing = False
if testing:
    for link, full_share in mgr.all_links.items():

        sysout, syserr = mgr.remove_link(link)
        # both err and out should be blank

        print('rmdir out: {}'.format(sysout))
        print('rmdir err: {}'.format(syserr))

        continue

exit(0)

答案2

如果你无法在程序设置中更改文件夹,那么你可能什么也做不了,只能在 Linux PC 上创建 iSCSI 目标(文件目标可能,以保持分区完整)并使用 Windows VM 上的 iSCSI 启动器连接到它(MS iSCSI 启动器StarWind iSCSI 启动器)。

或者,可以对程序进行十六进制编辑并将其指向映射的网络驱动器,但这需要一些技巧。

更新:我找到了一个解决方案,它看起来很有希望并且不需要处理 iSCSI:有没有办法将 UNC 路径映射到 Windows 2003 上的本地文件夹?。它看起来mklink能够映射网络共享(真丢脸,我应该检查一下的),如果失败了,你可以尝试适用于 Windows XP 的符号链接驱动程序

相关内容